package com.easylinkin.linkappapi.elasticsearch.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.StopWatch;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.analyze.entity.AnalyzeDateSource;
import com.easylinkin.linkappapi.analyze.entity.DdeviceVo;
import com.easylinkin.linkappapi.analyze.mapper.AnalyzeDateSourceMapper;
import com.easylinkin.linkappapi.analyze.service.AnalyzeDateSourceService;
import com.easylinkin.linkappapi.calculaterule.entity.CalculateConfigAttribute;
import com.easylinkin.linkappapi.calculaterule.mapper.CalculateRuleMapper;
import com.easylinkin.linkappapi.calculaterule.service.CalculateConfigAttributeService;
import com.easylinkin.linkappapi.calculaterule.service.CalculateConfigService;
import com.easylinkin.linkappapi.common.constant.CommonConstant;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.common.model.SortModel;
import com.easylinkin.linkappapi.common.utils.CommonUtils;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.common.utils.NumberUtil;
import com.easylinkin.linkappapi.common.utils.StringUtil;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.device.config.DeviceConfigUtil;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.entity.vo.DeviceQueryVo;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.deviceattribute.entity.DeviceAttribute;
import com.easylinkin.linkappapi.deviceattribute.service.DeviceAttributeService;
import com.easylinkin.linkappapi.elasticsearch.constant.EsConstantEnum;
import com.easylinkin.linkappapi.elasticsearch.constant.EsQueryString;
import com.easylinkin.linkappapi.elasticsearch.entity.*;
import com.easylinkin.linkappapi.elasticsearch.mapper.ElasticsearchRealMapper;
import com.easylinkin.linkappapi.elasticsearch.service.IBankAlarmRuleService;
import com.easylinkin.linkappapi.elasticsearch.service.IEService;
import com.easylinkin.linkappapi.elasticsearch.util.EsCalendar;
import com.easylinkin.linkappapi.elasticsearch.util.EsDeviceCollectUtil;
import com.easylinkin.linkappapi.iaqi.entity.IaqiStatistic;
import com.easylinkin.linkappapi.openapi.service.OpenApiDataVService;
import com.easylinkin.linkappapi.openapi.util.HttpClientUtil;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.space.entity.LinkappArea;
import com.easylinkin.linkappapi.space.mapper.LinkappAreaMapper;
import com.easylinkin.linkappapi.space.service.LinkappAreaService;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.mapper.LinkappTenantMapper;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import com.easylinkin.linkappapi.webcammanage.util.YYSHDateUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.max.ParsedMax;
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHitsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * es数据分析主要流程是：1 构建业务 2 根据业务构建条件  3 对查询结果进行解析处理
 */
@Service
public class EsServiceImpl extends ServiceImpl<ElasticsearchRealMapper, DeviceEnergyReal> implements IEService {

    private static final String TIME_FORMAT = " yyyy-MM-dd HH:mm:ss";
    private static final String DEVICE_ENERGY_COLLECTION_INDEX = "device-energy-collection";
    private static final int SIZE = 5000;

    /**
     * 四棵中学的租户id
     */
    @Value("${linkapp.siKeSchool.tenantId}")
    private String sikeTenantId;
    @Value("${linkapp.siKeSchool.water.propCode}")
    private String sikeWaterPropCode;
    @Value("${linkapp.siKeSchool.electricity.propCode}")
    private String sikeElectricityPropCode;

    @Resource
    private RedissonClient redisson;
    @Resource
    private DeviceConfigUtil deviceConfigUtil;

    @Value("${linkapp.openReceiving:true}")
    private Boolean openReceiving;
    /**
     * 保留小数位数
     */
    private static final int SCALE_4 = 4;
    private ESconfig eSconfig;
    @Autowired
    LinkappAreaMapper linkappAreaMapper;
    @Resource
    ElsClientBeanFactory elsClientBeanFactory;
    @Resource
    private CalculateConfigService calculateConfigService;

    @Autowired
    public void setESconfig(ESconfig eSconfig) {
        this.eSconfig = eSconfig;
    }

    private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(EsServiceImpl.class);
    private static final String BUDDING = "budding";
    private static final int SEVEN_DAY = 7;
    private static final int THIRTY_DAY = 30;
    private static final int AREA_TYPE = 1;
    private static final int DEVICE_TYPE = 2;

    @Autowired
    EsEnergyServiceImpl esEnergyServiceImpl;
    @Autowired
    EsBankServiceImpl esBankServiceImpl;
    @Autowired
    DeviceService deviceService;
    @Autowired
    LinkappAreaService linkappAreaService;
    @Autowired
    LinkappTenantService linkappTenantService;
    @Autowired
    AnalyzeDateSourceService analyzeDateSourceService;
    @Resource
    AnalyzeDateSourceMapper analyzeDateSourceMapper;
    @Resource
    CalculateRuleMapper calculateRuleMapper;
    @Autowired
    IBankAlarmRuleService iBankAlarmRuleService;
    @Resource
    CalculateConfigAttributeService calculateConfigAttributeService;
    @Resource
    DeviceAttributeService deviceAttributeService;
    @Resource
    DeviceMapper deviceMapper;

    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;

    @Resource
    private LinkappTenantMapper linkappTenantMapper;


    /**
     * queryModel 参数中放置对应的计算配置所配的 属性identifier
     */
    public void putQueryModelAttributeIdentifier(EsQuerymodel queryModel) {
        //   获取数据源 -》数据源查找计算配置 -> 计算配置查找 计算属性->取第一个计算属性
        if (!ObjectUtils.isEmpty(queryModel.getAttributeIdentifiers())) {
            return;
        }
        QueryWrapper qw = new QueryWrapper();
        qw.eq("analyze_data_source_id", queryModel.getDataSourceId());
        qw.select("device_attribute_id");
        List<CalculateConfigAttribute> calculateConfigAttributes = calculateConfigAttributeService.list(qw);
        if (ObjectUtils.isEmpty(calculateConfigAttributes)) {
            return;
        }
        qw = new QueryWrapper();
        qw.eq("id", calculateConfigAttributes.get(0).getDeviceAttributeId());
        qw.isNotNull("identifier");
        List<DeviceAttribute> deviceAttributes = deviceAttributeService.list(qw);

        if (deviceAttributes.size() != 1) {
            LOGGER.error("能源管理的查询或导出，期望由一个数据源查询到一个计算属性" + deviceAttributes);
            return;
        }
        List<String> attributeIdentifiers = deviceAttributes.stream().map(DeviceAttribute::getIdentifier).collect(Collectors.toList());
        queryModel.setAttributeIdentifiers(attributeIdentifiers);
    }

    /**
     * 设备能源实时查询 返回设备实时能源消耗列表
     */
    @Override
    public DeviceEnergyReal getRealDevicePage(EsQuerymodel esQuerymodel, EsResponsemodel esResponsemodel, Page page) {

        long currentTimeMillis = System.currentTimeMillis();
        Assert.notNull(esQuerymodel.getCurrentBeginTime(), "currentBeginTime 参数为空");
        Assert.notNull(esQuerymodel.getCurrentEndTime(), "currentEndTime 参数为空");
//        bug-服务器上的时间不是完全精确，现网大概率出现该问题
//        Assert.isTrue(System.currentTimeMillis() > esQuerymodel.getCurrentEndTime().getTime(), "结束时间需小于当前时间");
//        bug-修复- 202103-15
        Assert.isTrue(currentTimeMillis > esQuerymodel.getCurrentBeginTime().getTime(), "开始时间需小于当前时间");
        if (currentTimeMillis < esQuerymodel.getCurrentEndTime().getTime()) {
//            如果结束时间大于当前时间-则设置为当前时间
            esQuerymodel.setCurrentEndTime(new Date(System.currentTimeMillis()));
        }

        Assert.isTrue(esQuerymodel.getCurrentEndTime().getTime() > esQuerymodel.getCurrentBeginTime().getTime(), "开始时间需小于结束时间");

        //        根据数据源和 计算配置的类型查询不到计算配置则返回空
        if (ObjectUtils.isEmpty(calculateConfigService.getCalculateConfigs(esQuerymodel.getDataSourceId(), esQuerymodel.getUseKinds()))) {
            return null;
        }
//            新的每日能耗统计查询  每小时统计一次 查询方式：2021011814：查询2021-1-18 14:00 的 能耗统计 实时
        SearchResponse searchResponse = esEnergyServiceImpl.buildDayEnergyEachDeviceSearchResponse(esQuerymodel);
        DeviceEnergyReal deviceEnergyReal = esEnergyServiceImpl.resolvingDayEnergyEachDeviceList(searchResponse, esQuerymodel);

        deviceEnergyReal = filterDeviceAndfillDeviceBasicInfo(deviceEnergyReal, new Device().setAreaPath(esQuerymodel.getAreaPath()).setCode(esQuerymodel.getDeviceCode()));
        return deviceEnergyReal;
    }

    /**
     * 过滤设备（设备编号,设备区域） 并且填充设备基本信息
     *
     * @param deviceEnergyReal vo
     * @param device           设备编号 设备区域过滤条件
     */
    private DeviceEnergyReal filterDeviceAndfillDeviceBasicInfo(DeviceEnergyReal deviceEnergyReal, Device device) {
        if (deviceEnergyReal == null) {
            return null;
        }
        Set<String> deviceCodes = deviceEnergyReal.getItems().stream().map(DeviceEnergyRealItem::getDeviceCode).collect(Collectors.toSet());
        List<Device> deviceList = deviceService.selectDevicesByCodes(deviceCodes, device);
        if (ObjectUtils.isEmpty(deviceEnergyReal.getItems())) {
            return deviceEnergyReal;
        }
//        填充 设备基本信息,查询到设备信息的才保留，没有查到舍弃
        List<DeviceEnergyRealItem> items2 = new ArrayList<>();
        for (DeviceEnergyRealItem item : deviceEnergyReal.getItems()) {
            for (Device device1 : deviceList) {
                if (item.getDeviceCode() != null && item.getDeviceCode().equals(device1.getCode())) {
                    item.setDevice(device1);
                    items2.add(item);
                }
            }
        }
        deviceEnergyReal.setItems(items2);
        return deviceEnergyReal;
    }


    /**
     * 设备能耗历史数据获取
     *
     * @author tongjie
     */
    @Override
    public Map<String, List<DeviceEnergyReal>> getDeviceHistoricDataFromES(EsQuerymodel queryModel) throws JsonProcessingException {
        //构建查询
        SearchResponse searchResponse = esEnergyServiceImpl.buildDayEnergyEachDeviceSearchResponse(getProjectId(), queryModel);
        //解析查询
        Map<String, List<DeviceEnergyReal>> map = esEnergyServiceImpl.resolveHistoricEnergyEachDeviceList(searchResponse, queryModel);

        Assert.notEmpty(map, "Data can be exported is empty");
        return map;
    }

    /**
     * 获取设备能耗统计
     */
    public List<EsEtlStatisticModel> getDeviceEnergyStatistic(EsQuerymodel queryModel) {
        //根据数据源获得计算规则ID
        List<CalculateRuleOut> calculateRuleOutList = calculateRuleMapper.getRuleInfoByDataSourceId(queryModel.getDataSourceId());

        if (null == calculateRuleOutList || calculateRuleOutList.size() == 0) {
            LOGGER.info("查询计算规则ID为空数据源为：" + queryModel.getDataSourceId());
            return null;
        }
        CalculateRuleOut calculateRuleOut = calculateRuleOutList.get(0);
        String projectIndex = "aep_etl_" + calculateRuleOut.getProjectId();
        // 构建查询  queryTimeStart 格式： 2021-03-04 00:00:00
        Date queryTimeStart = queryModel.getCurrentBeginTime();
        queryTimeStart.setTime(queryTimeStart.getTime() + 24 * 60 * 60 * 1000);
        Date queryTimeEnd = queryModel.getCurrentEndTime();
        queryTimeEnd.setTime(queryTimeEnd.getTime() + 24 * 60 * 60 * 1000);
        SearchResponse searchResponse = esBankServiceImpl.getEsHistogramStatistic(projectIndex, queryModel.getDataSourceId(), calculateRuleOut.getCalculateConfigId(), queryModel.getUseKinds(), DateUtil.getYYYYMMDDHHMMSSDate(queryTimeStart), DateUtil.getYYYYMMDDHHMMSSDate(queryTimeEnd));

        //对ES查询结果数据进行解析
        List<EsEtlStatisticModel> dayEnergyEachDateSourceList = esBankServiceImpl.analysisEsHistogramStatistic(searchResponse);
        //        只保留 calcTime 是 00结尾的 比如 2021012500
        dayEnergyEachDateSourceList = dayEnergyEachDateSourceList.stream().filter(e -> e.getCalcTime() % 100 == 0).collect(Collectors.toList());
//        对要导出数据格式化
        Set<String> dataSourceIds = dayEnergyEachDateSourceList.stream().map(EsEtlStatisticModel::getDataSourceId).collect(Collectors.toSet());
        QueryWrapper<AnalyzeDateSource> qw = new QueryWrapper<>();
        qw.select("datasource_name");
        qw.in("id", dataSourceIds);
        List<AnalyzeDateSource> analyzeDateSources = analyzeDateSourceService.list();
        Map<String, String> idToDatasourceNameMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(analyzeDateSources)) {
            idToDatasourceNameMap = analyzeDateSources.stream().collect(Collectors.toMap(AnalyzeDateSource::getId, AnalyzeDateSource::getDatasourceName));
        }

        for (EsEtlStatisticModel esEtlStatisticModel : dayEnergyEachDateSourceList) {
            if (esEtlStatisticModel.getEtlValue() != null) {
                double d = new BigDecimal(esEtlStatisticModel.getEtlValue()).setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue();
                esEtlStatisticModel.setEtlValue(d);
            }
            esEtlStatisticModel.setDataSource(idToDatasourceNameMap.get(esEtlStatisticModel.getDataSourceId()));
        }

        return dayEnergyEachDateSourceList;
    }


    /**
     * 日周月 统计
     *
     * @author tongjie
     */
    @Override
    public DayWeekMonthStatisticModel dayWeekMonthStatistic(EsQuerymodel esQuerymodel) {
        Assert.hasLength(esQuerymodel.getDataSourceId(), "数据源id 为空");
        DayWeekMonthStatisticModel dayWeekMonthStatisticModel = new DayWeekMonthStatisticModel();
        if (ObjectUtils.isEmpty(calculateConfigService.getCalculateConfigs(esQuerymodel.getDataSourceId(), esQuerymodel.getUseKinds()))) {
            return null;
        }

//        设置 今昨日的
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

//        今日的 需单独查询
        esQuerymodel.setCurrentBeginTime(calendar.getTime());
        esQuerymodel.setCurrentEndTime(new Date());
        dayWeekMonthStatisticModel.setTodaySum(NumberUtil.doubleRoundHalfUp(getPeriodMax(esQuerymodel), 3));

        esQuerymodel.setCurrentBeginTime(calendar.getTime());
        esQuerymodel.setCurrentEndTime(new Date(calendar.getTime().getTime() + 24 * 60 * 60 * 1000));
        dayWeekMonthStatisticModel.setToday(new Date());
        CustomPeriodStatisticModel customPeriodStatisticModel = customPeriodStatistic(esQuerymodel);
        dayWeekMonthStatisticModel.setYesterdaySum(customPeriodStatisticModel.getLastCustomPeriodSum());
        dayWeekMonthStatisticModel.setYesterdayChainRatio(customPeriodStatisticModel.getLastCustomPeriodChainRatio());
//        设置周的
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
//        如果是周日 往前推6天
        if (dayOfWeek == 1) {
            calendar.add(Calendar.DAY_OF_MONTH, -6);
        } else {
            calendar.add(Calendar.DAY_OF_MONTH, 2 - dayOfWeek);
        }
        esQuerymodel.setCurrentBeginTime(calendar.getTime());
        Date date2 = new Date(calendar.getTime().getTime() + 7 * 24 * 60 * 60 * 1000);
        esQuerymodel.setCurrentEndTime(date2);
        customPeriodStatisticModel = customPeriodStatistic(esQuerymodel);
        dayWeekMonthStatisticModel.setThisWeekDayStart(calendar.getTime());
        dayWeekMonthStatisticModel.setThisWeekDayEnd(new Date(date2.getTime() - 24 * 60 * 60 * 1000));
        dayWeekMonthStatisticModel.setThisWeekSum(BigDecimal.valueOf(customPeriodStatisticModel.getThisCustomPeriodSum()).add(BigDecimal.valueOf(dayWeekMonthStatisticModel.getTodaySum())).doubleValue());
        dayWeekMonthStatisticModel.setLastWeekSum(customPeriodStatisticModel.getLastCustomPeriodSum());
        dayWeekMonthStatisticModel.setLastWeekChainRatio(customPeriodStatisticModel.getLastCustomPeriodChainRatio());

//        设置月的
        calendar.set(Calendar.DATE, 1);
        esQuerymodel.setCurrentBeginTime(calendar.getTime());
        calendar.add(Calendar.MONTH, 1);
        esQuerymodel.setCurrentEndTime(calendar.getTime());

        dayWeekMonthStatisticModel.setThisMonthSum(BigDecimal.valueOf(getPeriodSum(esQuerymodel)).add(BigDecimal.valueOf(dayWeekMonthStatisticModel.getTodaySum())).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue());

        dayWeekMonthStatisticModel.setThisMonthDayStart(esQuerymodel.getCurrentBeginTime());
        dayWeekMonthStatisticModel.setThisMonthDayEnd(new Date(esQuerymodel.getCurrentEndTime().getTime() - 24 * 60 * 60 * 1000));

        calendar.add(Calendar.MONTH, -1);
        esQuerymodel.setCurrentEndTime(calendar.getTime());
        calendar.add(Calendar.MONTH, -1);
        esQuerymodel.setCurrentBeginTime(calendar.getTime());
        dayWeekMonthStatisticModel.setLastMonthSum(NumberUtil.doubleRoundHalfUp(getPeriodSum(esQuerymodel), 3));

        esQuerymodel.setCurrentEndTime(calendar.getTime());
        calendar.add(Calendar.MONTH, -1);
        esQuerymodel.setCurrentBeginTime(calendar.getTime());
        double beforeLastValue = getPeriodSum(esQuerymodel);
        if (beforeLastValue != 0d && dayWeekMonthStatisticModel.getLastMonthSum() != null) {
            double ratio = (dayWeekMonthStatisticModel.getLastMonthSum() - beforeLastValue) / beforeLastValue;
            dayWeekMonthStatisticModel.setLastMonthChainRatio(new BigDecimal(ratio).multiply(new BigDecimal(100L)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
        }

        return dayWeekMonthStatisticModel;
    }

    @Override
    public CustomPeriodStatisticModel customPeriodStatistic(EsQuerymodel esQuerymodel) {
        if (ObjectUtils.isEmpty(calculateConfigService.getCalculateConfigs(esQuerymodel.getDataSourceId(), esQuerymodel.getUseKinds()))) {
            return null;
        }
        CustomPeriodStatisticModel customPeriodStatisticModel = new CustomPeriodStatisticModel();
        customPeriodStatisticModel.setThisCustomPeriodSum(NumberUtil.doubleRoundHalfUp(getPeriodSum(esQuerymodel), 3));
        rollSetPreviousPeriod(esQuerymodel);
        customPeriodStatisticModel.setLastCustomPeriodSum(NumberUtil.doubleRoundHalfUp(getPeriodSum(esQuerymodel), 3));
        rollSetPreviousPeriod(esQuerymodel);
        double beforeLastValue = getPeriodSum(esQuerymodel);
        if (beforeLastValue != 0d && customPeriodStatisticModel.getLastCustomPeriodSum() != null) {
            double ratio = (customPeriodStatisticModel.getLastCustomPeriodSum() - beforeLastValue) / beforeLastValue;
            customPeriodStatisticModel.setLastCustomPeriodChainRatio(new BigDecimal(ratio).multiply(BigDecimal.valueOf(100L)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            LOGGER.info("beforeLastValue:" + beforeLastValue + " ,lastCustomPeriodChainRatio:" + ratio);
        }

        return customPeriodStatisticModel;
    }

    @Override
    public CustomPeriodStatisticModel customPeriodStatistic2(EsQuerymodel esQuerymodel) {
        Assert.notNull(esQuerymodel.getQueryTimeStart(), "queryTimeStart 参数为空");
        Assert.notNull(esQuerymodel.getQueryTimeEnd(), "queryTimeEnd 参数为空");
        if (ObjectUtils.isEmpty(calculateConfigService.getCalculateConfigs(esQuerymodel.getDataSourceId(), esQuerymodel.getUseKinds()))) {
            return null;
        }
        esQuerymodel.setCurrentBeginTime(DateUtil.getDateFromYYYYMMDD(esQuerymodel.getQueryTimeStart()));
        esQuerymodel.setCurrentEndTime(new Date(DateUtil.getDateFromYYYYMMDD(esQuerymodel.getQueryTimeEnd()).getTime() + 24 * 60 * 60 * 1000));

        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        double todayValue = 0d;
        if (esQuerymodel.getCurrentEndTime().getTime() > calendar.getTimeInMillis()) {
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            todayValue = getPeriodMax(new EsQuerymodel().setCurrentBeginTime(calendar.getTime()).setCurrentEndTime(new Date()).setDataSourceId(esQuerymodel.getDataSourceId()).setUseKinds(esQuerymodel.getUseKinds()));
        }

        CustomPeriodStatisticModel customPeriodStatisticModel = customPeriodStatistic(esQuerymodel);

        customPeriodStatisticModel.setThisCustomPeriodSum(BigDecimal.valueOf(customPeriodStatisticModel.getThisCustomPeriodSum()).add(BigDecimal.valueOf(todayValue)).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue());

        return customPeriodStatisticModel;
    }

    /**
     * 设置到上一个查询时间段，每调用一次 时期往上滚动
     *
     * @param esQuerymodel 查询参数
     */
    private void rollSetPreviousPeriod(EsQuerymodel esQuerymodel) {
        Assert.notNull(esQuerymodel.getCurrentBeginTime(), "getPeriodSum:查询参数 起始日期 为空");
        Assert.notNull(esQuerymodel.getCurrentEndTime(), "getPeriodSum:查询参数 结束日期 为空");
        Date date1 = esQuerymodel.getCurrentBeginTime();
        Date date2 = esQuerymodel.getCurrentEndTime();
        long periodTime = date2.getTime() - date1.getTime();

        Date end = date1;
        Date start = new Date(end.getTime() - periodTime);
        esQuerymodel.setCurrentBeginTime(start);
        esQuerymodel.setCurrentEndTime(end);
    }

    /**
     * 查询指定时间的 最大值
     */
    private double getPeriodMax(EsQuerymodel esQuerymodel) {
//        必须参数校验
        Assert.notNull(esQuerymodel.getCurrentBeginTime(), "getPeriodMax:查询参数 起始日期 为空");
        Assert.notNull(esQuerymodel.getCurrentEndTime(), "getPeriodMax:查询参数 结束日期 为空");
        Assert.hasLength(esQuerymodel.getDataSourceId(), "getPeriodMax:查询参数 数据源id 为空");

        RestHighLevelClient rhlClient = eSconfig.getFactory().getRhlClient();
        String projectIndex = "aep_etl_" + getProjectId();
        // 构建查询  "currentEndTime": "2021-02-26 14:23:20"
        String queryTimeStartStr = DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentBeginTime());
        String queryTimeEndStr = DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentEndTime());
        QueryBuilder rangeQuery = QueryBuilders.rangeQuery("calcTime").lte(Long.parseLong(queryTimeEndStr)).gt(Long.parseLong(queryTimeStartStr));

        QueryBuilder dataSourceIdQuery = QueryBuilders.termQuery("dataSourceId", esQuerymodel.getDataSourceId());
        // 组装ES请求数据

        QueryBuilder boolQuery = QueryBuilders.boolQuery().must(rangeQuery).must(dataSourceIdQuery);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery).size(0);
        MaxAggregationBuilder maxAggregationBuilder = AggregationBuilders.max("day_max").field("day");
        searchSourceBuilder.aggregation(maxAggregationBuilder);
        SearchRequest searchRequest = new SearchRequest(projectIndex).source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = rhlClient.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("方法getPeriodSum查询ES请求数据：" + searchRequest.toString());

        } catch (Exception e) {
            LOGGER.error("getPeriodSum ES查询异常", e);
        }
        if (searchResponse == null || searchResponse.getAggregations() == null || searchResponse.getAggregations().getAsMap() == null || searchResponse.getAggregations().getAsMap().get("day_max") == null) {
            return 0d;
        }
        ParsedMax ps = (ParsedMax) searchResponse.getAggregations().getAsMap().get("day_max");
        LOGGER.info("方法getPeriodSum查询ES请求结果：" + ps.getValue());
        double value = ps.getValue();
        if (value == Double.NEGATIVE_INFINITY) {
            return 0d;
        }
        return value;

    }

    /**
     * 方法用途：查询 一段时期内的 用量和
     * </br>
     * 参数说明： dataSourceId 数据源id
     * </br>
     * 参数 currentBeginTime  ~ currentEndTime 起始时间到结束时间日期，前闭后闭，能源统计
     * </br>
     * 例如：入参"currentBeginTime": "2021-02-26 00:00:00", "currentEndTime": "2021-03-02 00:00:00"
     * </br>
     * 计算的是 2021-02-26 00:00:00 到 2021-03-02 00:00:00 之间的能源用量，2-26 2-27 2-28 3-1 4天的
     * </br>
     *
     * @param esQuerymodel 查询参数
     */
    private double getPeriodSum(EsQuerymodel esQuerymodel) {
//        必须参数校验
        Assert.notNull(esQuerymodel.getCurrentBeginTime(), "getPeriodSum:查询参数 起始日期 为空");
        Assert.notNull(esQuerymodel.getCurrentEndTime(), "getPeriodSum:查询参数 结束日期 为空");
        Assert.hasLength(esQuerymodel.getDataSourceId(), "getPeriodSum:查询参数 数据源id 为空");

        RestHighLevelClient rhlClient = eSconfig.getFactory().getRhlClient();
        String projectIndex = "aep_etl_" + getProjectId();
        // 构建查询  "currentBeginTime": "2021-02-26 00:00:00"
        String queryTimeStartStr = DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentBeginTime());
        String queryTimeEndStr = DateUtil.getYYYYMMDDHHNumberString(esQuerymodel.getCurrentEndTime());

        QueryBuilder rangeQuery = QueryBuilders.rangeQuery("calcTime").lte(Long.parseLong(queryTimeEndStr)).gt(Long.parseLong(queryTimeStartStr));
        QueryBuilder dataSourceIdQuery = QueryBuilders.termQuery("dataSourceId", esQuerymodel.getDataSourceId());
        // 组装ES请求数据
        QueryBuilder filterQuery = QueryBuilders.boolQuery().filter(QueryBuilders.scriptQuery(new Script("doc.calcTime.value % 100 == 0")));
        QueryBuilder boolQuery = QueryBuilders.boolQuery().must(rangeQuery).must(dataSourceIdQuery).filter(filterQuery);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQuery).size(0);
        SumAggregationBuilder sum = AggregationBuilders.sum("day_sum").field("day");
        searchSourceBuilder.aggregation(sum);
        SearchRequest searchRequest = new SearchRequest(projectIndex).source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = rhlClient.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("方法getPeriodSum查询ES请求数据：" + searchRequest.toString());

        } catch (Exception e) {
            LOGGER.error("getPeriodSum ES查询异常", e);
        }
        if (searchResponse == null || searchResponse.getAggregations() == null || searchResponse.getAggregations().getAsMap() == null || searchResponse.getAggregations().getAsMap().get("day_sum") == null) {
            return 0d;
        }
        ParsedSum ps = (ParsedSum) searchResponse.getAggregations().getAsMap().get("day_sum");
        LOGGER.info("方法getPeriodSum查询ES请求结果：" + ps.getValue());
        return ps.getValue();
    }

    /**
     * 柱状图统一数据接口 查询参数   数据源id  能源类型 包含 过去7日的用电量  30日的用电量  汇总聚合
     */
    @Override
    public EsHistogramResponseModel histogramStatistic(EsQuerymodel queryModel) throws JsonProcessingException {
        EsHistogramResponseModel esResponsemodel = new EsHistogramResponseModel();
        if (ObjectUtils.isEmpty(calculateConfigService.getCalculateConfigs(queryModel.getDataSourceId(), queryModel.getUseKinds()))) {
            LOGGER.info("查询计算规则ID为空数据源为：" + queryModel.getDataSourceId());
            return esResponsemodel;
        }
        //根据数据源获得计算规则ID
        List<CalculateRuleOut> calculateRuleOutList = calculateRuleMapper.getRuleInfoByDataSourceId(queryModel.getDataSourceId());
        CalculateRuleOut calculateRuleOut = new CalculateRuleOut();
        if (null != calculateRuleOutList && calculateRuleOutList.size() > 0) {
            calculateRuleOut = calculateRuleOutList.get(0);
        } else {
            LOGGER.info("查询计算规则ID为空数据源为：" + queryModel.getDataSourceId());
            return esResponsemodel;
        }
        String projectIndex = "aep_etl_" + calculateRuleOut.getProjectId();
        String gteString = EsCalendar.getBefreYear();
        String lteString = EsCalendar.getStartTimeOfDay(-1);
        String todayString = EsCalendar.getStartTimeOfDay(0).substring(0, 10);//截取年月日
        //构建查询
        SearchResponse searchResponse = esBankServiceImpl.getEsHistogramStatistic(projectIndex, queryModel.getDataSourceId(), calculateRuleOut.getCalculateConfigId(), queryModel.getUseKinds(), gteString, lteString);
        //对ES查询结果数据进行解析
        List<EsEtlStatisticModel> dayEnergyEachDateSourceList = esBankServiceImpl.analysisEsHistogramStatistic(searchResponse);
//        只保留 calcTime 是 00结尾的 比如 2021012500
        dayEnergyEachDateSourceList = dayEnergyEachDateSourceList.stream().filter(e -> e.getCalcTime() % 100 == 0).collect(Collectors.toList());

        if (null != dayEnergyEachDateSourceList && dayEnergyEachDateSourceList.size() > 0) {
            //获取今日的数据
            List<EsEtlStatisticModel> todaySourceList = dayEnergyEachDateSourceList.parallelStream().filter(x -> x.haveDayTime().equals(todayString)).collect(Collectors.toList());
            double todayValue = 0;
            if (null != todaySourceList && todaySourceList.size() > 0) {
                todayValue = todaySourceList.get(0).getEtlValue();
            }

            Map<String, DoubleSummaryStatistics> dayCollect = dayEnergyEachDateSourceList.stream().collect(Collectors.groupingBy(EsEtlStatisticModel::haveDayTime, Collectors.summarizingDouble(EsEtlStatisticModel::getEtlValue)));
            // 按照月聚合映射
            Map<String, DoubleSummaryStatistics> monthCollect = dayEnergyEachDateSourceList.stream().collect(Collectors.groupingBy(EsEtlStatisticModel::haveMonthTime, Collectors.summarizingDouble(EsEtlStatisticModel::getEtlValue)));
            //过去12个月
            Map<String, Double> lastYearMonth = getLastYearMonth(monthCollect);
            //过去30天
            Map<String, Double> lastThirtyDay = getLastDay(dayCollect, THIRTY_DAY);
            //过去7天
            Map<String, Double> lastSevenDay = getLastDay(dayCollect, SEVEN_DAY);
            double onDuty7Day = 0;
            for (Map.Entry<String, Double> m : lastSevenDay.entrySet()) {
                onDuty7Day += m.getValue().intValue();
            }
            double onDuty30Day = 0;
            for (Map.Entry<String, Double> m : lastThirtyDay.entrySet()) {
                onDuty30Day += m.getValue().intValue();
            }
            esResponsemodel.setTodayValue(todayValue);
            esResponsemodel.setLastSevenDaySum(onDuty7Day);
            esResponsemodel.setLastThirtyDaySum(onDuty30Day);
            esResponsemodel.setLastYearMonthMap(lastYearMonth);
            esResponsemodel.setLastThirtyDay(lastThirtyDay);
            esResponsemodel.setLastSevenDay(lastSevenDay);
        }

        return esResponsemodel;
    }

    /**
     * 环比通用接口 参数：数据源id 能源类型  时间段  环比类型 返回：
     */
    @Override
    public MomStatisticModel getMomStatisticData(EsQuerymodel queryModel) throws JsonProcessingException, ParseException {
        MomStatisticModel model = new MomStatisticModel(queryModel.getDataSourceId(), queryModel.getUseKinds(), queryModel.getCurrentBeginTime(), queryModel.getCurrentEndTime(), queryModel.getMomStatisticKind());
        model.initialize();
        if (ObjectUtils.isEmpty(calculateConfigService.getCalculateConfigs(queryModel.getDataSourceId(), queryModel.getUseKinds()))) {
            return model;
        }
        String gteString = EsCalendar.getAfterDayByDate(model.getMomBeginTime(), 0);
//        由于 2021012700 代表的是26 的数据 1 -》2
        String lteString = EsCalendar.getAfterDayByDate(model.getCurrentEndTime(), 2);
        //计算规则ID(001)
        List<CalculateRuleOut> calculateRuleOutList = calculateRuleMapper.getRuleInfoByDataSourceId(queryModel.getDataSourceId());
        CalculateRuleOut calculateRuleOut = new CalculateRuleOut();
        if (null != calculateRuleOutList && calculateRuleOutList.size() > 0) {
            calculateRuleOut = calculateRuleOutList.get(0);
            String projectIndex = "aep_etl_" + calculateRuleOut.getProjectId();
            SearchResponse searchResponse = esBankServiceImpl.getEsHistogramStatistic(projectIndex, queryModel.getDataSourceId(), calculateRuleOut.getCalculateConfigId(), queryModel.getUseKinds(), gteString, lteString);
            List<EsEtlStatisticModel> dayEnergyEachDeviceList = esBankServiceImpl.analysisEsHistogramStatistic(searchResponse);
            //        只保留 calcTime 是 00结尾的 比如 2021012500
            dayEnergyEachDeviceList = dayEnergyEachDeviceList.stream().filter(e -> e.getCalcTime() % 100 == 0).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(dayEnergyEachDeviceList)) {
                model.constructStaticSequenResult(dayEnergyEachDeviceList);
            }
        } else {
            LOGGER.info("查询计算规则ID为空数据源为：" + queryModel.getDataSourceId());
            return model;
        }
        return model;
    }

    private Map<String, Double> getLastYearMonth(Map<String, DoubleSummaryStatistics> monthCollect) {
        String month;
        Map<String, Double> map = new LinkedHashMap<>();
        for (int i = 11; i >= 0; i--) {
            month = EsCalendar.getBefreMonth(i);
            DoubleSummaryStatistics sunObj = monthCollect.get(month);
            if (monthCollect.get(month) != null) {
                map.put(month, (double) Math.round(sunObj.getSum() * 100) / 100);
            } else {
                map.put(month, Double.valueOf(0));
            }
        }
        return map;
    }

    private Map<String, Double> getLastDay(Map<String, DoubleSummaryStatistics> monthCollect, int j) {
        String day;
        Map<String, Double> map = new LinkedHashMap<>();
        for (int i = j; i > 0; i--) {
            day = EsCalendar.getBeforeDay(i);
            DoubleSummaryStatistics sunObj = monthCollect.get(day);
            if (monthCollect.get(day) != null) {
                map.put(day, (double) Math.round(sunObj.getSum() * 100) / 100);
            } else {
                map.put(day, Double.valueOf(0));
            }
        }
        return map;
    }


    /**
     * 这方法非常重要，通过数据源，能源类型，筛选出参与筛选的设备
     *
     * @param dataSourceId 数据源id
     */
    @Override
    public List<String> getDeviceCodes(String dataSourceId) {
        AnalyzeDateSource analyzeDateSource = new AnalyzeDateSource();
        analyzeDateSource.setId(dataSourceId);
        analyzeDateSource = analyzeDateSourceService.getDateSourceById(analyzeDateSource);
        Assert.notNull(analyzeDateSource, "选择的数据源不存在");
        Assert.notNull(analyzeDateSource.getDatasourceType(), "数据源类型为空");
        List<String> deviceCodeList = new ArrayList<>();
        List<String> unitCodeList = elsClientBeanFactory.getUnitCodeList(dataSourceId);
//        计算配置 ‘4号楼用电情况统计’ 少了这种型号的
//        unitCodeList.add("DLQ-RS485-15-0123");

        DdeviceVo deviceVo = new DdeviceVo();
        deviceVo.setDeviceUnitCodeList(unitCodeList);
//        数据源类型是区域
        if (analyzeDateSource.getDatasourceType() == AREA_TYPE) {
            LinkappArea dateSourceArea = new LinkappArea();
            String areaId = analyzeDateSource.getAreaId();
            dateSourceArea.setId(areaId);
            dateSourceArea = linkappAreaService.selectLinkappArea(dateSourceArea);
            Assert.notNull(dateSourceArea, "数据源的区域为空");
            deviceVo.setAreaPath(dateSourceArea.getAreaPath());
            //加一个查询字段,设备类型集合
            List<Device> deviceList = deviceService.selectDevices(deviceVo);
            if (!ObjectUtils.isEmpty(deviceList)) {
                deviceCodeList = deviceList.stream().map(Device::getCode).collect(Collectors.toList());
            }
        }
//        数据源类型是指定设备
        if (analyzeDateSource.getDatasourceType() == DEVICE_TYPE) {
            if (!ObjectUtils.isEmpty(analyzeDateSource.getDeviceIdString())) {
                deviceVo.setDataSourceid(analyzeDateSource.getId());
                deviceVo.setBuddingFlag(BUDDING);
                List<Device> deviceList = analyzeDateSourceMapper.getDeviceByDataSourceId(deviceVo);
                if (!ObjectUtils.isEmpty(deviceList)) {
                    deviceCodeList = deviceList.stream().map(Device::getCode).collect(Collectors.toList());
                }

            }
        }
        return deviceCodeList;
    }


    protected String getProjectId() {
        LinkappTenant currentTenant = linkappTenantService.currentTenant();
        Assert.notNull(currentTenant, "当前租户为空");
        Assert.notNull(currentTenant.getProjectId(), "当前租户项目id为空");
        Long projectId = CommonUtils.parseLong(currentTenant.getProjectId());
        return String.valueOf(projectId);
    }

    @Override
    public SearchHits getDeviceDataListFromES(String deviceCode, Long projectId, int index, int size, String beginTime, String endTime, List<String> necessaryFieldList, List<SortModel> sorts) {
        // 查询条件，可以参考官网手册
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.filter(QueryBuilders.termQuery("deviceCode", deviceCode));
        boolQuery.must(QueryBuilders.matchPhraseQuery("projectId", projectId));
        boolQuery.filter(QueryBuilders.rangeQuery("createTime").gte(beginTime).lte(endTime).format("yyyy-MM-dd HH:mm:ss"));
        QueryBuilder queryBuilder = QueryBuilders.boolQuery();
        if (!CollectionUtils.isEmpty(necessaryFieldList)) {
            necessaryFieldList.forEach(item -> {
//                属性状态值 挪到了 data之下
                QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                ((BoolQueryBuilder) queryBuilder).should(existQuery);
            });
        }
//        解决bug 【ID1003343】【设备详情】设备历史数据比设备管理平台的少一条最新的
        index--;
        // es 6.0以上支持RestHighLevelClient
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

//        获取索引,这个月和上个月的 deviceflow-202101,deviceflow-202012
//        String indices = EsCalendar.getDeviceFlowIndex(6);
        String indices = EsCalendar.getDeviceFlowIndex(beginTime, endTime);
        SearchRequest searchRequest = new SearchRequest(indices);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        Assert.notNull(deviceCode, "查询Es，设备编号不能为空!");
//        boolQuery.must(QueryBuilders.matchPhraseQuery("deviceCode", deviceCode));
//        boolQuery.must(queryBuilder);
        boolQuery.filter(queryBuilder);

        searchSourceBuilder.query(boolQuery);
        String[] includeFields = new String[]{};
        String[] excludeFields = new String[]{"sourceRef"};
        searchSourceBuilder.fetchSource(includeFields, excludeFields);
//        ES 本身限制 最大只能查询一万条
        Assert.isTrue(size <= 10000, "超过最大查询数，请缩小查询范围");
        searchSourceBuilder.from(index * size).size(size);  //取数大小
        if (ObjectUtils.isEmpty(sorts)) {
            // 如果无排序规则 使用默认倒序
            searchSourceBuilder.sort("createTime", SortOrder.DESC);
        } else {
            for (SortModel sort : sorts) {
                searchSourceBuilder.sort(sort.getField(), SortOrder.fromString(sort.getSortRule()));
            }
        }
        searchRequest.source(searchSourceBuilder);
        try {
            long startTime = System.currentTimeMillis();
            // 查询结果
            LOGGER.info("设备数据详情ES请求数据：" + searchRequest);
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            long sysEndTime = System.currentTimeMillis();
            LOGGER.info("查询用时:" + (sysEndTime - startTime));
            return hits;
        } catch (Exception e) {
            LOGGER.error("getDeviceDataListFromES 搜索ES数据,索引:" + indices + "异常:{}", e);
            e.printStackTrace();
        } finally {
        }
        return null;
    }


    @Override
    public SearchHits getDeviceDataListFromESByDeviceName(Map<String, String> likeQueryMap, int index, int size, String beginTime, String endTime, List<String> necessaryFieldList, String indices) {
        QueryBuilder boolquery = QueryBuilders.boolQuery();
        if (!CollectionUtils.isEmpty(necessaryFieldList)) {
            necessaryFieldList.forEach(item -> {
                QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                ((BoolQueryBuilder) boolquery).should(existQuery);
            });
        }
        index--;
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

        if (StringUtils.isEmpty(indices)) {
            indices = EsCalendar.getDeviceFlowIndex(2);
        }

//        为兼容 10月份的索引mapping改造，两类查询合二为一
        String[] indexs = indices.split(",");
        Set<String> indexsSetOver201110 = new HashSet<>();
//        具有超过十月份的索引
        for (String s : indexs) {
            if (s.compareTo("deviceflow-202110") >= 0) {
                indexsSetOver201110.add(s);
            }
        }
        String searchIndex;
        String queryContainKeyword;
        if (indexsSetOver201110.isEmpty()) {
            queryContainKeyword = "data.capture_time.keyword";
            searchIndex = indices;
        } else {
            searchIndex = org.apache.commons.lang3.StringUtils.join(indexsSetOver201110, ",");
            queryContainKeyword = "data.capture_time";

        }
        //  ---end       为兼容 10月份的索引mapping改造，两类查询合二为一

        SearchRequest searchRequest = new SearchRequest(searchIndex);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 查询条件，可以参考官网手册
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (likeQueryMap != null && likeQueryMap.size() > 0) {
            for (Map.Entry<String, String> entry : likeQueryMap.entrySet()) {
                if (entry.getValue() != null && !"".equals(entry.getValue().trim())) {
                    if ("projectId".equals(entry.getKey())) {
                        boolQuery.must(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
                    } else {
                        boolQuery.must(QueryBuilders.wildcardQuery(entry.getKey(), "*" + entry.getValue() + "*"));
                    }
                }
            }
        }

//        boolQuery.must(boolquery);
        boolQuery.filter(boolquery);
        if ((beginTime != null && beginTime != "") && (endTime != null && endTime != "")) {
            boolQuery.filter(QueryBuilders.rangeQuery(queryContainKeyword).gte(beginTime).lte(endTime).format("yyyy-MM-dd HH:mm:ss"));
        }
        searchSourceBuilder.query(boolQuery);
        String[] includeFields = new String[]{};
        String[] excludeFields = new String[]{"sourceRef"};
        searchSourceBuilder.fetchSource(includeFields, excludeFields);
        //ES 本身限制 最大只能查询一万条
        Assert.isTrue(size <= 10000, "超过最大查询数，请缩小查询范围");
        //取数大小
        searchSourceBuilder.from(index * size).size(size);
        //倒叙
        searchSourceBuilder.sort(queryContainKeyword, SortOrder.DESC);
        searchRequest.source(searchSourceBuilder);
        try {
            long startTime = System.currentTimeMillis();
            // 查询结果
            LOGGER.info("设备数据详情ES请求数据：" + searchRequest.toString());
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            long sysEndTime = System.currentTimeMillis();
            LOGGER.info("查询用时:" + (sysEndTime - startTime));
            return hits;
        } catch (Exception e) {
            LOGGER.error("getDeviceDataListFromESByDeviceName 搜索ES数据异常:{}", e);
            e.printStackTrace();
        } finally {
        }
        return null;
    }

    @Override
    public SearchResponse getDeviceFlowListFromES(Map<String, Object> likeQueryMap, int index, int size, TimeRange timeRange, List<String> necessaryFieldList, SortField sortField, TermsAggregationBuilder termsAggregationBuilder) {
        SearchResponse deviceFlowListFromES = getDeviceFlowListFromES(likeQueryMap, index, size, timeRange, necessaryFieldList, sortField, termsAggregationBuilder, null);
        return deviceFlowListFromES;
    }

    @Override
    public SearchResponse getDeviceFlowListFromES(Map<String, Object> likeQueryMap, int index, int size, TimeRange timeRange, List<String> necessaryFieldList, SortField sortField, TermsAggregationBuilder termsAggregationBuilder, String indices) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (!CollectionUtils.isEmpty(necessaryFieldList)) {
            if (necessaryFieldList.size() == 1) {
                necessaryFieldList.forEach(item -> {
                    QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                    boolQuery.must(existQuery);
                });
            } else {
                BoolQueryBuilder childBoolQuery = QueryBuilders.boolQuery();
                necessaryFieldList.forEach(item -> {
                    QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                    childBoolQuery.should(existQuery);
                });
                boolQuery.must(childBoolQuery);
            }
        }
        index--;
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

        if (StringUtils.isEmpty(indices)) {
            indices = EsCalendar.getDeviceFlowIndex(2);
        }
        SearchRequest searchRequest = new SearchRequest(indices);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 查询条件，可以参考官网手册

        if (likeQueryMap != null && likeQueryMap.size() > 0) {
            for (Map.Entry<String, Object> entry : likeQueryMap.entrySet()) {
                if (entry.getValue() != null && entry.getValue() instanceof String) {
                    if ("projectId".equals(entry.getKey())) {
                        boolQuery.must(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
                    } else if ("deviceCodeTerm".equals(entry.getKey())) {
                        boolQuery.must(QueryBuilders.termQuery("deviceCode", entry.getValue()));
                    } else {
                        boolQuery.must(QueryBuilders.wildcardQuery(entry.getKey(), "*" + entry.getValue() + "*"));
                    }
                } else if (entry.getValue() != null && entry.getValue() instanceof List) {
                    List<String> deviceCodeList = (List) entry.getValue();
                    boolQuery.must(QueryBuilders.termsQuery(entry.getKey(), deviceCodeList));
                }
            }
        }

        if ((timeRange != null)) {
            boolQuery.filter(QueryBuilders.rangeQuery(timeRange.getFieldName()).gte(timeRange.getStartTime()).lte(timeRange.getEndTime()).format("yyyy-MM-dd HH:mm:ss"));
        }

        searchSourceBuilder.query(boolQuery);
        if (termsAggregationBuilder != null) {
            searchSourceBuilder.aggregation(termsAggregationBuilder);
        }
        String[] includeFields = new String[]{};
        String[] excludeFields = new String[]{"sourceRef"};
        searchSourceBuilder.fetchSource(includeFields, excludeFields);
        //取数大小
        searchSourceBuilder.from(index * size).size(size);

        if (sortField != null) {
            searchSourceBuilder.sort(sortField.getFieldName(), sortField.getSortOrder());
        }

        searchRequest.source(searchSourceBuilder);
        if (size >= 10000) {
            searchRequest.scroll(TimeValue.timeValueMinutes(CommonConstant.TWO));
        }
        try {
            // 查询结果
            LOGGER.info("设备数据详情ES请求数据：" + searchRequest.toString());
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            return searchResponse;
        } catch (Exception e) {
            LOGGER.error("getDeviceFlowListFromES 搜索ES数据异常:{}", e);
            e.printStackTrace();
        } finally {
        }
        return null;
    }

    @Override
    public SearchResponse getScrollData(String scrollId) throws IOException {
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();
        SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId).scroll(TimeValue.timeValueMinutes(CommonConstant.TWO));
        ;
        SearchResponse searchResponse = hclient.searchScroll(searchScrollRequest);
        LOGGER.info("scroll took=" + searchResponse.getTook());
        LOGGER.info("scroll shard.total=" + searchResponse.getTotalShards());
        LOGGER.info("scroll shard.successful=" + searchResponse.getSuccessfulShards());
        return searchResponse;
    }

    /**
     * @param projectId    未使用
     * @param deviceCode
     * @param gteString
     * @param lteString
     * @param selectFields
     * @return
     */
    @Override
    public SearchHits getHistoricDataCurrDay(String projectId, String deviceCode, String gteString, String lteString, String selectFields) {

        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

//        获取索引,这个月和上个月的 deviceflow-202101,deviceflow-202012
        String indices = EsCalendar.getDeviceFlowIndex(2);
        SearchRequest searchRequest = new SearchRequest(indices);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.matchQuery("deviceCode", deviceCode));
        boolQuery.filter(QueryBuilders.rangeQuery("createTime").gte(gteString).lte(lteString).format(TIME_FORMAT));
        if (null != selectFields && !"".equals(selectFields)) {
            List<String> necessarFieldList = Arrays.asList(selectFields.split(","));
            BoolQueryBuilder filedBoolQuery = QueryBuilders.boolQuery();
            if (!CollectionUtils.isEmpty(necessarFieldList)) {
                necessarFieldList.forEach(item -> {
                    QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                    filedBoolQuery.should(existQuery);
                });
            }
            boolQuery.must(filedBoolQuery);
        }

        searchSourceBuilder.query(boolQuery);
        searchSourceBuilder.sort("createTime", SortOrder.ASC); //倒叙
        searchSourceBuilder.size(SIZE);
        searchRequest.source(searchSourceBuilder);
        try {
            long startTime = System.currentTimeMillis();
            // 查询结果
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("ES查询请求数据：" + searchRequest.toString());
            SearchHits hits = searchResponse.getHits();
            long sysEndTime = System.currentTimeMillis();
            LOGGER.info("查询用时:" + (sysEndTime - startTime));
            return hits;
        } catch (Exception e) {
            LOGGER.error("getHistoricDataCurrDay 搜索ES数据,索引:" + indices + "异常:{}", e);
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public void exportBankExcel(String useKinds, HttpServletRequest request, HttpServletResponse response) {
        List<AnalyzeDateSource> analyzeDateSourceList = analyzeDateSourceService.getDateSourceList();
        List<BankExcelModel> result = new ArrayList<>();
        // 获取当月第一天和最后一天
        Calendar cale = null;
        cale = Calendar.getInstance();
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
        Date firstday, lastday;
        // 获取上月的第一天
        cale = Calendar.getInstance();
        cale.add(Calendar.MONTH, -1);
        cale.set(Calendar.DAY_OF_MONTH, 1);
        firstday = cale.getTime();
        // 获取上月的最后一天
        cale = Calendar.getInstance();
        cale.add(Calendar.MONTH, 0);
        cale.set(Calendar.DAY_OF_MONTH, 0);
        lastday = cale.getTime();
        String currentBeginTime = format.format(firstday);
        String currentEndTime = format.format(lastday);
        // 获取上上月的第一天
        cale = Calendar.getInstance();
        cale.add(Calendar.MONTH, -2);
        cale.set(Calendar.DAY_OF_MONTH, 1);
        String momBeginTime = format.format(cale.getTime());
        // 获取上上月的最后一天
        cale = Calendar.getInstance();
        cale.add(Calendar.MONTH, -1);
        cale.set(Calendar.DAY_OF_MONTH, 0);
        String momEndTime = format.format(cale.getTime());
        List<Double> momRatList = new ArrayList<>();

        for (AnalyzeDateSource analyzeDateSource : analyzeDateSourceList) {
            EsQuerymodel queryModel = new EsQuerymodel();
            queryModel.setMomStatisticKind(2);
            queryModel.setUseKinds(useKinds);
            queryModel.setDataSourceId(analyzeDateSource.getId());
            queryModel.setCurrentBeginTime(firstday);
            queryModel.setCurrentEndTime(lastday);
            try {
                MomStatisticModel momStatisticModel = getMomStatisticData(queryModel);
                BankExcelModel bankExcelModel = new BankExcelModel();
                if (!momStatisticModel.getStaticResultMap().isEmpty()) {
                    bankExcelModel.setName(analyzeDateSource.getDatasourceName());
                    double curr = momStatisticModel.getStaticResultMap().get("sumEnergy").get("current");
                    double mom = momStatisticModel.getStaticResultMap().get("sumEnergy").get("mom");
                    bankExcelModel.setCurrNum(curr + "");
                    bankExcelModel.setMomNum(mom + "");
                    double momRat = 100;
                    if (mom > 0) {
                        momRat = (curr - mom) * 100 / mom;
                    }
                    BigDecimal bg = new BigDecimal(momRat);
                    double doubleValue = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                    momRatList.add(doubleValue);
                    bankExcelModel.setMomRat(doubleValue + "%");
                    if (momRat > 0) {
                        bankExcelModel.setTrend("上升");
                    } else {
                        bankExcelModel.setTrend("下降");
                    }
                    Page page = new Page();
                    page.setCurrent(1);
                    page.setSize(10);
                    BankAlarmNotice alarmNotice = new BankAlarmNotice();
                    alarmNotice.setDataSourceId(analyzeDateSource.getId());
                    IPage<BankAlarmNotice> noticeIPage = iBankAlarmRuleService.getAlarmNoticePage(page, alarmNotice);
                    bankExcelModel.setAlarmNum(noticeIPage.getTotal() + "");
                    result.add(bankExcelModel);
                }

            } catch (JsonProcessingException e) {
                e.printStackTrace();
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }

        if (momRatList.size() > 0) {
            result.add(new BankExcelModel());
            DoubleSummaryStatistics statistics = momRatList.stream().mapToDouble(Number::doubleValue).summaryStatistics();
            momRatList.sort(Comparator.reverseOrder());
            BankExcelModel bankExcelRat = new BankExcelModel();
            bankExcelRat.setName("最大环比值");
            bankExcelRat.setCurrNum(statistics.getMax() + "%");
            bankExcelRat.setMomNum("最小环比值");
            bankExcelRat.setMomRat(statistics.getMin() + "%");
            bankExcelRat.setTrend("环比平均值");
            bankExcelRat.setAlarmNum(statistics.getAverage() + "%");
            result.add(bankExcelRat);
        }
        List<String> stringList = new ArrayList<>();
        stringList.add("统计时间段");
        stringList.add(momBeginTime + "-" + currentEndTime);
        stringList.add("统计数据");
        if ("electric".equals(useKinds)) {
            stringList.add("能耗(kwh)");
        } else {
            stringList.add("人流量(人数)");
        }

        String keyValue = "客户名称:name,本月周期数据(" + currentBeginTime + "-" + currentEndTime + "):currNum," + "上月周期数据(" + momBeginTime + "-" + momEndTime + "):momNum,环比值:momRat,趋势:trend,周告警次数:alarmNum";
        String title = "银行贷后报表统计报告";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil.getOutputStream(request, response, fileName);
            ExcelTools.exportBankExcel(outputStream, keyValue, result, ExcelConstant.XLS, title, stringList);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            LOGGER.error("excel导出失败");
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    @Override
    public void exportEnergyReport(EsQuerymodel queryModel, HttpServletRequest request, HttpServletResponse response) throws IOException {

        Assert.notNull(queryModel.getCurrentBeginTime(), "起始时间参数为空");
        Assert.notNull(queryModel.getCurrentEndTime(), "结束时间参数为空");

        Map paramMap = new HashMap();
        paramMap.put("workbook", new HSSFWorkbook());
        paramMap.put("title", "总统计报表导出数据");
        paramMap.put("sheetName", "总统计报表");

        exportEnergyStatisticReport(queryModel, paramMap);
        exportEnergyEachReport(queryModel, paramMap);

        //        生成表格
        String fileName = "energyReport_" + DateUtil.getyyyyMMddHHmmssSSSNumberString(new Date()) + ".xls";
        OutputStream outputStream = OutputStreamUtil.getOutputStream(request, response, fileName);

        Workbook wb = (Workbook) paramMap.get("workbook");
        wb.write(outputStream);
        response.flushBuffer();
        outputStream.close();

    }

    /**
     * 导出 每个设备的sheet
     *
     * @param queryModel 查询条件
     */
    private void exportEnergyEachReport(EsQuerymodel queryModel, Map paramMap) throws JsonProcessingException {
        //        获取单个设备的 不同日期的能耗分布统计
        Map<String, List<DeviceEnergyReal>> eachDeviceRecordMap = getDeviceHistoricDataFromES(queryModel);

        //   列表第二以及以后的sheet的各个设备的数据
        String keyValueEachDevice = "统计时间:createTimeStr,设备编码:deviceCode,设备名称:deviceName,设备型号:uniqueMark" + ",型号名称:deviceUnitName,空间区域:areaPath,当日用电量(kwh):dayEnergy,当前日累计用电量(kwh):sumEnergy";

        Collection<List<DeviceEnergyReal>> eachDeviceRecordValues = eachDeviceRecordMap.values();
        Map<String, String> deviceCode2NameMap = new TreeMap<>();
        for (List<DeviceEnergyReal> eachDeviceRecordValue : eachDeviceRecordValues) {
            for (DeviceEnergyReal deviceEnergyReal : eachDeviceRecordValue) {
                deviceCode2NameMap.put(deviceEnergyReal.getDeviceCode(), deviceEnergyReal.getDeviceName());
            }
        }
        if (!ObjectUtils.isEmpty(deviceCode2NameMap)) {
            QueryWrapper<Device> qw = new QueryWrapper<>();
            qw.select("code", "area_path", "name");
            qw.in("code", deviceCode2NameMap.keySet());
            List<Device> devices = deviceService.list(qw);
            Map<String, Device> deviceCodeToDeviceMap = new HashMap<>();
            for (Device device : devices) {
                deviceCodeToDeviceMap.put(device.getCode(), device);
            }
            eachDeviceRecordMap.forEach((deviceCode, deviceEnergyReals) -> {
                paramMap.put("sheetName", StringUtil.coverSheetName(deviceCode2NameMap.get(deviceCode)));
                paramMap.put("title", deviceCode2NameMap.get(deviceCode));
                for (DeviceEnergyReal deviceEnergyReal : deviceEnergyReals) {
                    deviceEnergyReal.setSumEnergy(getFormatScale4RoundHalfUpBigDecimal(deviceEnergyReal.getSumEnergy()));
                    deviceEnergyReal.setDayEnergy(getFormatScale4RoundHalfUpBigDecimal(deviceEnergyReal.getDayEnergy()));
                    deviceEnergyReal.setCreateTimeStr(DateUtil.getYYYYMMDDDate(deviceEnergyReal.getCreateTime()));
                    deviceEnergyReal.setAreaPath(deviceCodeToDeviceMap.get(deviceEnergyReal.getDeviceCode()).getAreaPath());
                }
                ExcelTools.preExportExcelOneSheet(paramMap, keyValueEachDevice, deviceEnergyReals);
            });
        }
    }

    /**
     * 导出 统计 sheet
     *
     * @param queryModel 查询条件
     */
    private void exportEnergyStatisticReport(EsQuerymodel queryModel, Map paramMap) {
        //        获取总的设备能耗统计
        List<EsEtlStatisticModel> statisticData = getDeviceEnergyStatistic(queryModel);
//        排序
        statisticData = statisticData.stream().sorted((a, b) -> {
            if (a.getDateTime() == null || b.getDateTime() == null) {
                return 0;
            }
            return b.getDateTime().compareTo(a.getDateTime());
        }).collect(Collectors.toList());
        //  列表统计第一个sheet
        String keyValueStatistic = "统计时间:calcDate,统计数据源:dataSource,当日用电量(kwh):etlValue";
        ExcelTools.preExportExcelOneSheet(paramMap, keyValueStatistic, statisticData);
    }

    /**
     * 获取 4位小数的 四舍五入的 double
     *
     * @param value 输入值
     */
    private Double getFormatScale4RoundHalfUpBigDecimal(Double value) {
        if (value == null) {
            return null;
        }
        return new BigDecimal(value).setScale(SCALE_4, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    @Override
    public List<Map<String, Object>> parseResultFromSearchHit(SearchHit[] results) {
        List<Map<String, Object>> records = new ArrayList<>();
        for (SearchHit hit : results) {
            Object data = hit.getSourceAsMap().get("data");
            if (ObjectUtils.isEmpty(data)) {
                continue;
            }
            Map<String, Object> sourceMap1 = (Map<String, Object>) data;
            for (Entry<String, Object> item : sourceMap1.entrySet()) {
                try {
//                       如果是时间戳的long 类型 返回， 时间格式 1.590406860678E12 原时间
                    if (item.getKey().toLowerCase().contains("time")) {
                        Double doubleTimeValue = Double.parseDouble(String.valueOf(item.getValue()));
                        if (String.valueOf(doubleTimeValue.longValue()).length() == 13) {
                            sourceMap1.put(item.getKey(), DateUtil.getYYYYMMDDHHMMSSDate(new Date(doubleTimeValue.longValue())));
                        }
                    }
                } catch (Exception e) {
                    LOGGER.info("时间转换有异常：{}", e.getMessage());
                }
            }

            Map<String, Object> sourceMap = hit.getSourceAsMap();
            List<String> basicInfos = Arrays.asList("deviceCode", "deviceName", "deviceTypeName", "deviceUnitCode", "createTime");
            sourceMap.forEach((key, value) -> {
                if (basicInfos.indexOf(key) != -1) {
                    sourceMap1.put(key, value);
                }
            });
            records.add(sourceMap1);
        }
        return records;
    }

    /***
     * 根据设备查询能耗统计
     */
    @Override
    public Map<String, Object> energyStatisticsByDeviceCode(EsQuerymodel esQuerymodel) {
        if (esQuerymodel == null || esQuerymodel.getDeviceCodeList() == null || esQuerymodel.getDeviceCodeList().size() <= 0) {
            return null;
        }
        Map<String, Map<String, Object>> statisticsByDeviceCode = energyStatisticsByDeviceCodeEs(esQuerymodel);
        if (statisticsByDeviceCode == null || statisticsByDeviceCode.size() <= 0) {
            return null;
        }
        EsQuerymodel esQuerymodelQuery = new EsQuerymodel();
        BeanUtils.copyProperties(esQuerymodel, esQuerymodelQuery);

        esQuerymodelQuery.setCurrentBeginTime(null);
        esQuerymodelQuery.setCurrentEndTime(esQuerymodel.getCurrentBeginTime());
        Map<String, Object> minDate = energyStatisticsByDeviceCodeEsMax(esQuerymodelQuery);
        //获取时间段
        Map<String, Object> resultMap = getHourConllect(statisticsByDeviceCode, DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentBeginTime()), DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentEndTime()), esQuerymodel, minDate);
        return resultMap;
    }

    @Override
    public SfQueryModelResult energyAnalyzeBySpace(EsQuerymodel esQuerymodel) {
        SfQueryModelResult smr = new SfQueryModelResult();
        //获取空间下一级所有区域
        Map<String, List<String>> spaceDeviceCodeMap = linkappAreaService.getSpaceRefDeviceCodeData();

        Map<String, Double> areaEnergyMap = new LinkedHashMap<>();
        //调用energyStatisticsByDeviceCode方法获取设备组的能耗
        for (Map.Entry<String, List<String>> entry : spaceDeviceCodeMap.entrySet()) {
            if (entry.getValue() == null) {
                areaEnergyMap.put(entry.getKey(), 0.0);
            } else {
                EsQuerymodel childEsQuery = new EsQuerymodel();
                childEsQuery.setDeviceCodeList(entry.getValue());
                childEsQuery.setCurrentBeginTime(esQuerymodel.getCurrentBeginTime());
                childEsQuery.setCurrentEndTime(esQuerymodel.getCurrentEndTime());
                List<String> attributeIdentifiers = new ArrayList<>();
                attributeIdentifiers.add("positive_active_energy");
                childEsQuery.setAttributeIdentifiers(attributeIdentifiers);
                childEsQuery.setMomStatisticKind(esQuerymodel.getMomStatisticKind());
                Map<String, Object> stringObjectMap = energyStatisticsByDeviceCode(childEsQuery);
                if (stringObjectMap == null || stringObjectMap.isEmpty()) {
                    areaEnergyMap.put(entry.getKey(), 0.0);
                } else {
                    Double positive_active_energy_all = 0.0;
                    for (Map.Entry<String, Object> entryChild : stringObjectMap.entrySet()) {
                        Map<String, Double> ahd = (Map<String, Double>) entryChild.getValue();
                        Double positive_active_energy = ahd.get("positive_active_energy");
                        positive_active_energy_all = new BigDecimal(positive_active_energy_all).add(new BigDecimal(positive_active_energy)).doubleValue();
                    }
                    areaEnergyMap.put(entry.getKey(), NumberUtil.doubleRoundHalfUp(positive_active_energy_all, 2));
                }
            }
        }
        smr.setData(areaEnergyMap);
        Map<String, Double> stringDoubleMap = CommonUtils.sortMap(areaEnergyMap);
        smr.setSortData(stringDoubleMap);
        return smr;
    }

    private List<Device> getDeviceList(String areaId) {
        QueryWrapper qw = new QueryWrapper();
        qw.eq("delete_state", 1);
        qw.eq("area_id", areaId);
        List<Device> list = deviceMapper.selectList(qw);
        return list;
    }

    @Override
    public SfQueryModelResult energyAnalyzeByRoleAuthority(EsQuerymodel esQuerymodel) {
        SfQueryModelResult smr = new SfQueryModelResult();
        //获取区域关联的所有设备
        List<Device> deviceList = getDeviceList(esQuerymodel.getAreaId());

        String yearStart = DateUtil.getCurrentYear() + "-01-01 00:00:00";
        String yearEnd = DateUtil.getCurrentYear() + "-12-31 23:59:59";
        String monthStart = YYSHDateUtil.getFirstDayOfMonth() + DateUtil.DATE_START_HMS;
        String monthEnd = DateUtil.getLastDayOfMonth(Integer.valueOf(DateUtil.getCurrentYear()), Integer.valueOf(DateUtil.getCurrentMM())) + DateUtil.DATE_END_HMS;

        List<String> months = YYSHDateUtil.getMonths(yearStart, yearEnd);
        List<String> days = YYSHDateUtil.getDays(monthStart, monthEnd);
        List<String> hours = YYSHDateUtil.getHourByDay(DateUtil.getCurrentDateStr(DateUtil.DATE_TIME_FORMAT_DAY));

        if (org.apache.commons.lang3.ObjectUtils.isEmpty(deviceList)) {
            Map<String, Double> yearMap = fillData(months, null);
            smr.setYearData(keyConvertData(yearMap, 2));

            Map<String, Double> monthMap = fillData(days, null);
            smr.setMonthData(keyConvertData(monthMap, 1));

            Map<String, Double> dayMap = fillData(hours, null);
            smr.setDayData(keyConvertData(dayMap, 0));

        } else {
            List<String> codeList = deviceList.stream().map(Device::getCode).collect(Collectors.toList());
            EsQuerymodel esQuery = new EsQuerymodel();
            Date startDate = DateUtil.getDateFromFormatStr(yearStart, DateUtil.DATE_TIME_FORMAT_DEFAULT);
            Date endDate = new Date();
            esQuery.setCurrentBeginTime(startDate);
            esQuery.setCurrentEndTime(endDate);
            esQuery.setMomStatisticKind(CommonConstant.FOUR);
            Map<String, Object> yearEnergyMap = getEnergyDataMap(esQuery, codeList);
            Map<String, Double> yearMap = fillData(months, yearEnergyMap);
            smr.setYearData(keyConvertData(yearMap, 2));
            Collection<Double> yearValues = yearMap.values();
            Double yearMaxData = Collections.max(yearValues);
            smr.setYearMaxData(yearMaxData);

            startDate = DateUtil.getDateFromFormatStr(monthStart, DateUtil.DATE_TIME_FORMAT_DEFAULT);
            esQuery.setCurrentBeginTime(startDate);
            esQuery.setCurrentEndTime(endDate);
            esQuery.setMomStatisticKind(CommonConstant.THREE);
            Map<String, Object> monthEnergyMap = getEnergyDataMap(esQuery, codeList);
            Map<String, Double> monthMap = fillData(days, monthEnergyMap);
            smr.setMonthData(keyConvertData(monthMap, 1));
            Collection<Double> monthValues = monthMap.values();
            Double monthMaxData = Collections.max(monthValues);
            smr.setMonthMaxData(monthMaxData);

            startDate = DateUtil.getDateFromFormatStr(DateUtil.getCurrentDateStr(DateUtil.DATE_TIME_FORMAT_DAY) + DateUtil.DATE_START_HMS, DateUtil.DATE_TIME_FORMAT_DEFAULT);
            endDate = DateUtil.getDateFromFormatStr(DateUtil.getCurrentDateStr(DateUtil.DATE_TIME_FORMAT_DAY) + DateUtil.DATE_END_HMS, DateUtil.DATE_TIME_FORMAT_DEFAULT);
            esQuery.setCurrentBeginTime(startDate);
            esQuery.setCurrentEndTime(endDate);
            esQuery.setMomStatisticKind(CommonConstant.FIVE);
            Map<String, Object> dayEnergyMap = getEnergyDataMapTemp2(esQuery, codeList);
            Map<String, Double> dayMap = fillData(hours, dayEnergyMap);
            smr.setDayData(keyConvertData(dayMap, 0));
            Collection<Double> dayValues = dayMap.values();
            Double dayMaxData = Collections.max(dayValues);
            smr.setDayMaxData(dayMaxData);
        }
        return smr;
    }

    private Map<String, Object> getEnergyDataMap(EsQuerymodel esQuery, List<String> codeList) {
        List<String> attributeIdentifiers = new ArrayList<>();
        attributeIdentifiers.add("positive_active_energy");
        esQuery.setAttributeIdentifiers(attributeIdentifiers);
        esQuery.setDeviceCodeList(codeList);
        Map<String, Object> energyMap = energyStatisticsByDeviceCode(esQuery);
        return energyMap;
    }

    private Map<String, Object> getEnergyDataMapTemp2(EsQuerymodel esQuery, List<String> codeList) {
        List<String> attributeIdentifiers = new ArrayList<>();
        attributeIdentifiers.add("positive_active_energy");
        esQuery.setAttributeIdentifiers(attributeIdentifiers);
        esQuery.setDeviceCodeList(codeList);
        Map<String, Object> energyMap = energyStatisticsByDeviceCodeTemp2(esQuery);
        return energyMap;
    }

    private Map<String, Object> energyStatisticsByDeviceCodeTemp2(EsQuerymodel esQuerymodel) {
        if (esQuerymodel == null || esQuerymodel.getDeviceCodeList() == null || esQuerymodel.getDeviceCodeList().size() <= 0) {
            return null;
        }
        Map<String, Map<String, Object>> statisticsByDeviceCode = energyStatisticsByDeviceCodeEs(esQuerymodel);
        if (statisticsByDeviceCode == null || statisticsByDeviceCode.size() <= 0) {
            return null;
        }
        EsQuerymodel esQuerymodelQuery = new EsQuerymodel();
        BeanUtils.copyProperties(esQuerymodel, esQuerymodelQuery);
        String monthStart = YYSHDateUtil.getFirstDayOfMonth() + DateUtil.DATE_START_HMS;
        Date currentBeginTime = DateUtil.getDateFromFormatStr(monthStart, DateUtil.DATE_TIME_FORMAT_DEFAULT);
        esQuerymodelQuery.setCurrentBeginTime(currentBeginTime);
        esQuerymodelQuery.setCurrentEndTime(esQuerymodel.getCurrentBeginTime());
        Map<String, Object> minDate = energyStatisticsByDeviceCodeEsMax(esQuerymodelQuery);
        //获取时间段
        Map<String, Object> resultMap = getHourConllect(statisticsByDeviceCode, DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentBeginTime()), DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentEndTime()), esQuerymodel, minDate);
        return resultMap;
    }


    private Map<String, Double> fillData(List<String> keyList, Map<String, Object> energyMap) {
        Map<String, Double> commonMap = new LinkedHashMap<>();
        if (energyMap == null || energyMap.isEmpty()) {
            for (String key : keyList) {
                commonMap.put(key, 0.0);
            }
        } else {
            for (String key : keyList) {
                if (energyMap.get(key) != null) {
                    commonMap.put(key, (Double) ((Map) energyMap.get(key)).get("positive_active_energy"));
                } else {
                    commonMap.put(key, 0.0);
                }
            }
        }
        return commonMap;
    }

    /**
     * 将map数据的key简化
     *
     * @param dataMap
     * @param timeFlag 0：时，1：日，2：月
     * @return
     */
    private Map<String, Double> keyConvertData(Map<String, Double> dataMap, int timeFlag) {
        Map<String, Double> commonMap = new LinkedHashMap<>();
        for (Map.Entry<String, Double> entry : dataMap.entrySet()) {
            String key = entry.getKey();
            if (timeFlag == 0) {
                key = key.substring(11);
                key += "时";
            } else if (timeFlag == 1) {
                key = key.substring(8);
                key += "日";
            } else if (timeFlag == 2) {
                key = key.substring(5);
                key += "月";
            }
            commonMap.put(key, entry.getValue());
        }
        return commonMap;
    }

    @Override
    public String getIndicesListByDate(String startTime, String endTime) {
        if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {
            return null;
        }
        List<String> months = YYSHDateUtil.getMonths(startTime, endTime);
        Collections.reverse(months);
        StringBuilder sb = new StringBuilder();
        months.forEach(month -> {
            sb.append(",").append("deviceflow-").append(month.replaceAll("-", ""));
        });
        String s = sb.toString();
        String indices = s.substring(1);
        return indices;
    }

    @Override
    public List<Map<String, Object>> getDeviceCollectDataAggFromEs(RequestModel<EsDeviceCollectModel> requestModel) {
        Long projectId = requestModel.getCustomQueryParams().getProjectId();
        Boolean useTenant = requestModel.getCustomQueryParams().getUseTenant();

        List<Map<String, Object>> dataList = new ArrayList<>();
        //String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();

        LinkappTenant currentTenant = linkappTenantService.currentTenant();
        Assert.notNull(currentTenant, "当前租户为空");
        Assert.notNull(currentTenant.getProjectId(), "当前租户项目id为空");

        //项目id
        if (useTenant) {
            //使用登录租户信息
            projectId = CommonUtils.parseLong(currentTenant.getProjectId());
        }
        if (projectId == null) {
            log.warn("--设备采集数据周期内按设备、采集类型、聚合类型分组统计项目id为空");
            return null;
        }

        //处理时间
        EsDeviceCollectUtil.initRangeTime(requestModel);

        //重装后的参数
        LOGGER.info("getDeviceCollectDataAggFromEs 重组后的参数:" + JSONObject.toJSONString(requestModel));

        //获取索引
        String indices = EsCalendar.getDeviceFlowIndex(requestModel.getCustomQueryParams().getStartTime(), requestModel.getCustomQueryParams().getEndTime());

        //ES查询请求对象
        SearchRequest searchRequest = new SearchRequest(indices);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));

        //组织查询条件对象
        QueryBuilder boolquery = QueryBuilders.boolQuery();
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

        // 查询条件，可以参考官网手册
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //增加项目ID过滤
        boolQuery.must(QueryBuilders.matchPhraseQuery("projectId", projectId));
        boolQuery.must(boolquery);

        //增加过滤条件
        //增加设备编码过滤
        String deviceCode = requestModel.getCustomQueryParams().getDeviceCode();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(deviceCode)) {
            boolQuery.filter(QueryBuilders.termQuery("deviceCode", deviceCode));
        }

        String ftm = "yyyy-MM-dd HH:mm:ss";
        String startTimeStr = cn.hutool.core.date.DateUtil.format(requestModel.getCustomQueryParams().getStartTime(), ftm);
        String endTimeStr = cn.hutool.core.date.DateUtil.format(requestModel.getCustomQueryParams().getEndTime(), ftm);
        //增加时间过滤
        boolQuery.filter(QueryBuilders.rangeQuery("createTime").gte(startTimeStr).lte(endTimeStr).format(ftm));

        //查询来源构建
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQuery);

        //设置包含字段、排除字段
        String[] includeFields = new String[]{};
        String[] excludeFields = new String[]{"sourceRef"};
        searchSourceBuilder.fetchSource(includeFields, excludeFields);

        // 取数大小
        int index = 0;
        int size = 0;
        searchSourceBuilder.from(index * size).size(size);

        //分组key
        List<String> groupKeyList = new ArrayList<>();
        //一级分组，按项目id分
        String rootGroupKey = "agg-projectId-groups";
        TermsAggregationBuilder projectGroupTerm = AggregationBuilders.terms(rootGroupKey).field("projectId");
        groupKeyList.add(rootGroupKey);
        //分组参数
        //聚合计算参数
        List<String> aggKeyList = new ArrayList<>();
        List<String> groupsList = requestModel.getCustomQueryParams().getGroupsList();
        if (CollectionUtil.isNotEmpty(groupsList)) {

            for (int i = 0; i < groupsList.size(); i++) {
                String groupField = groupsList.get(i);
                String groupKey = "agg-" + groupField + "-group";
                TermsAggregationBuilder groupTerm = AggregationBuilders.terms(groupKey).field(groupField);
                groupKeyList.add(groupKey);

                if (i == groupsList.size() - 1) {
                    //处理聚合计算语义
                    EsDeviceCollectUtil.initAggFun(groupTerm, requestModel, aggKeyList);
                }
                projectGroupTerm.subAggregation(groupTerm);
            }
        } else {
            EsDeviceCollectUtil.initAggFun(projectGroupTerm, requestModel, aggKeyList);
        }

        //查询对象增加计算条件
        searchSourceBuilder.aggregation(projectGroupTerm);
        searchRequest.source(searchSourceBuilder);

        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 查询结果
            LOGGER.info("设备数据详情ES请求searchSourceBuilder数据：" + searchSourceBuilder);
            LOGGER.info("设备数据详情ES请求数据：" + searchRequest);
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            stopWatch.stop();
            LOGGER.info("查询用时ms:" + stopWatch.getTotalTimeMillis());
            //解析查询结果
            dataList = EsDeviceCollectUtil.analyzeSearchResponse(searchResponse, groupKeyList, aggKeyList);


        } catch (Exception e) {
            LOGGER.error("getDeviceCollectDataAggFromEs 搜索ES数据,索引:" + indices + "异常:", e);
        }

        return dataList;
    }

    public static Map<String, Object> getHourConllect(Map<String, Map<String, Object>> statisticsByDeviceCode, String startHour, String endHour, EsQuerymodel esQuerymodel, Map<String, Object> minDate) {
        int dateInt = 0;
        String dateFormat = null;
        if (esQuerymodel.getMomStatisticKind() == 4) {
            dateInt = Calendar.MONTH;
            dateFormat = "yyyy-MM";
        } else if (esQuerymodel.getMomStatisticKind() == 5) {
            dateInt = Calendar.HOUR;
            dateFormat = "yyyy-MM-dd HH";
        } else {
            dateInt = Calendar.DATE;
            dateFormat = "yyyy-MM-dd";
        }
        List<String> hourByDay = OpenApiDataVService.getTimeBetween(startHour, endHour, dateFormat, dateInt);
        String finalDateFormat = dateFormat;
        int finalDateInt = dateInt;
        Map<String, Object> resultMap = new HashMap();

        hourByDay.stream().sorted((m1, m2) -> Long.compare(DateUtil.parse(m2, finalDateFormat).getTime(), DateUtil.parse(m1, finalDateFormat).getTime())).forEach(date -> {
            statisticsByDeviceCode.entrySet().stream().forEach(entity -> {
                //key 时间（时间戳），value deviceData
                Map<String, Object> deviceMap = entity.getValue();
                AtomicInteger i = new AtomicInteger();
                HashMap<String, Object> deviceAttrMap = new HashMap<>();
                String parse = String.valueOf(DateUtil.parse(date, finalDateFormat).getTime());
                //获取当前时间的设备属性
                HashMap<String, Object> dateMap = (HashMap<String, Object>) deviceMap.get(String.valueOf(parse));
                //获取前一个有值的最大值
                HashMap<String, Object> nextDateMap = new HashMap<>();
                if (dateMap != null) {
                    AtomicBoolean flag = new AtomicBoolean(false);
                    deviceMap.entrySet().stream().sorted((m1, m2) -> Long.compare(Long.valueOf(m2.getKey()), Long.valueOf(m1.getKey()))).forEach(map -> {
                        if (!parse.equals(map.getKey()) && !flag.get()) {
                            return;
                        }
                        if (!nextDateMap.isEmpty()) {
                            return;
                        }
                        if (!flag.get()) {
                            flag.set(true);
                            //当前值不设置，设置下一次值
                            return;
                        }
                        HashMap<String, Object> nextDateMapNotNull = (HashMap<String, Object>) map.getValue();
                        if (nextDateMapNotNull == null || nextDateMapNotNull.isEmpty()) {
                            return;
                        }
                        nextDateMap.putAll(nextDateMapNotNull);
                        i.getAndIncrement();
                    });
                    if (nextDateMap == null || nextDateMap.size() <= 0) {
                        //最后一个值
                        if (minDate != null) {
                            Object minDateMap = minDate.get(entity.getKey());
                            if (minDateMap != null) {
                                nextDateMap.putAll((HashMap<String, Object>) minDateMap);
                            }
                        } else {
                            nextDateMap.putAll(dateMap);
                            for (String field : esQuerymodel.getAttributeIdentifiers()) {
                                Object o = nextDateMap.get(field);
                                if (o == null) {
                                    continue;
                                }
                                nextDateMap.put(field, BigDecimal.ZERO);
                            }
                        }
                    }
                }
                //获取设备属性值
                for (String field : esQuerymodel.getAttributeIdentifiers()) {
                    if (nextDateMap == null || nextDateMap.get(field) == null || dateMap == null || dateMap.get(field) == null) {
                        deviceAttrMap.put(field, Double.valueOf(0));
                    } else {
                        //当前值减去下一个值，因为倒序
                        Double attr = Double.valueOf(String.valueOf(dateMap.get(field))) - Double.valueOf(String.valueOf(nextDateMap.get(field)));
                        BigDecimal b = new BigDecimal(attr);
                        deviceAttrMap.put(field, b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                    }
                }
                //总计设备属性值
                if (resultMap.get(date) == null) {
                    resultMap.put(date, deviceAttrMap);
                } else {
                    Map<String, Object> lastDeviceMap = (HashMap<String, Object>) resultMap.get(date);
                    for (String field : esQuerymodel.getAttributeIdentifiers()) {
                        if (lastDeviceMap.get(field) == null || deviceAttrMap.get(field) == null) {
                            continue;
                        }
                        Double attr = Double.valueOf(String.valueOf(lastDeviceMap.get(field))) + Double.valueOf(String.valueOf(deviceAttrMap.get(field)));
                        BigDecimal b = new BigDecimal(attr);
                        deviceAttrMap.put(field, b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                    }
                    resultMap.put(date, deviceAttrMap);
                }

//                deviceMap.entrySet().stream().forEach(map->{
//                    Date dateKey = new Date(Long.valueOf(map.getKey()));
//                    String format = DateUtil.format(dateKey, finalDateFormat);
//                    if(date.equals(format)){
//                        HashMap<String, Object> dateMap = (HashMap<String, Object>)deviceMap.get(map.getKey());
//                        if(dateMap == null){
//                            return;
//                        }
//                        //获取下个节点第一条或是最后一条
//                        HashMap<String, Object> nextDateMap=null;
//                        if(i.get() != hourByDay.size()){
//                            Calendar start = Calendar.getInstance();
//                            start.setTime(dateKey);
//                            start.add(finalDateInt,1);
//                            format=String.valueOf(start.getTimeInMillis());
//                            nextDateMap = (HashMap<String, Object>) deviceMap.get(format);
//                        }else{
//                            HashMap devicecodeMap=(HashMap<String, Object>)dataMax.get(entity.getKey());
//                            if(devicecodeMap == null){
//                                return;
//                            }
//                            format = DateUtil.format(esQuerymodel.getCurrentEndTime(), finalDateFormat);
//                            format = String.valueOf(DateUtil.parse(format,finalDateFormat).getTime());
//                            nextDateMap=(HashMap<String, Object>) devicecodeMap.get(format);
//                        }
//                        if(nextDateMap == null){
//                            return;
//                        }
//                        //获取设备属性值
//                        for (String field:esQuerymodel.getAttributeIdentifiers()){
//                            if(nextDateMap.get(field) == null || dateMap.get(field) == null){
//                                return;
//                            }else{
//                                Double attr = Double.valueOf(String.valueOf(nextDateMap.get(field))) - Double.valueOf(String.valueOf(dateMap.get(field)));
//                                BigDecimal b=new BigDecimal(attr);
//                                deviceAttrMap.put(field,b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue());
//                            }
//                        }
//                    }
//                });
//                //补零
//                if(deviceAttrMap.isEmpty()){
//                    for (String file : esQuerymodel.getAttributeIdentifiers()) {
//                        deviceAttrMap.put(file,Double.valueOf(0));
//                    }
//                }
//                //总计设备属性值
//                if(resultMap.get(date) == null){
//                    resultMap.put(date,deviceAttrMap);
//                }else{
//                    Map<String, Object> lastDeviceMap = (HashMap<String, Object>)resultMap.get(date);
//                    for (String field:esQuerymodel.getAttributeIdentifiers()){
//                        if(lastDeviceMap.get(field) == null || deviceAttrMap.get(field) == null){
//                            continue;
//                        }
//                        Double attr = Double.valueOf(String.valueOf(lastDeviceMap.get(field))) + Double.valueOf(String.valueOf(deviceAttrMap.get(field)));
//                        BigDecimal b=new BigDecimal(attr);
//                        deviceAttrMap.put(field,b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue());
//                    }
//                    resultMap.put(date,deviceAttrMap);
//                }
            });
        });
        LinkedHashMap<String, Object> map = null;
        String yyyymmddDate = DateUtil.getYYYYMMDDDate(new Date());
        if (esQuerymodel.getOrderBy() != null && esQuerymodel.getOrderBy() == 1) {
            map = resultMap.entrySet().stream().sorted((m1, m2) -> Long.compare(DateUtil.parse(m1.getKey(), finalDateFormat).getTime(), DateUtil.parse(m2.getKey(), finalDateFormat).getTime())).collect(Collectors.toMap(m -> m.getKey(), m -> m.getValue(), (key1, key2) -> key1, LinkedHashMap::new));
        } else {
            if (esQuerymodel.getMomStatisticKind() != 4 && !yyyymmddDate.equals(esQuerymodel.getQueryTimeEnd())) {
                map = resultMap.entrySet().stream().sorted((m1, m2) -> Long.compare(DateUtil.parse(m2.getKey(), finalDateFormat).getTime(), DateUtil.parse(m1.getKey(), finalDateFormat).getTime())).collect(Collectors.toMap(m -> m.getKey(), m -> m.getValue(), (key1, key2) -> key1, LinkedHashMap::new));
            } else {
                map = resultMap.entrySet().stream().sorted((m1, m2) -> Long.compare(DateUtil.parse(m1.getKey(), finalDateFormat).getTime(), DateUtil.parse(m2.getKey(), finalDateFormat).getTime())).collect(Collectors.toMap(m -> m.getKey(), m -> m.getValue(), (key1, key2) -> key1, LinkedHashMap::new));
            }
        }

        return map;
    }

    /***
     * 根据设备查询能耗统计
     */
    public Map<String, Map<String, Object>> energyStatisticsByDeviceCodeEs(EsQuerymodel esQuerymodel) {
        if (ObjectUtils.isEmpty(esQuerymodel.getAttributeIdentifiers())) {
            return null;
        }
        esQuerymodel.setQueryTimeEnd(DateUtil.getYYYYMMDDDate(esQuerymodel.getCurrentEndTime()));
        Date date = new Date();
        Calendar dateEnd = Calendar.getInstance();
        dateEnd.setTime(date);
        dateEnd.set(Calendar.HOUR_OF_DAY, 23);
        dateEnd.set(Calendar.MINUTE, 59);
        dateEnd.set(Calendar.SECOND, 59);
        date = dateEnd.getTime();
        if (date.before(esQuerymodel.getCurrentEndTime())) {
            esQuerymodel.setCurrentEndTime(date);
        }
        List<String> necessaryFieldList = esQuerymodel.getAttributeIdentifiers();
//        LinkappTenant currentTenant = linkappTenantService.currentTenant();
//        Assert.notNull(currentTenant, "当前租户为空");
//        Assert.notNull(currentTenant.getProjectId(), "当前租户项目id为空");
//        Long projectId = CommonUtils.parseLong(currentTenant.getProjectId());
        QueryBuilder boolquery = QueryBuilders.boolQuery();
        if (!CollectionUtils.isEmpty(necessaryFieldList)) {
            necessaryFieldList.forEach(item -> {
//                属性状态值 挪到了 data之下
                QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                ((BoolQueryBuilder) boolquery).should(existQuery);
            });
        }
        //        解决bug 【ID1003343】【设备详情】设备历史数据比设备管理平台的少一条最新的
        //index--;
        // es 6.0以上支持RestHighLevelClient
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

        DateHistogramInterval dateHistogramInterval = null;
        String dateFormat = null, indices = "";
        if (esQuerymodel.getMomStatisticKind() == 4) {
            dateHistogramInterval = DateHistogramInterval.MONTH;
            dateFormat = "yyyy-MM";
            indices += "deviceflow-" + DateUtil.format(esQuerymodel.getCurrentEndTime(), "yyyy") + "*";
        } else if (esQuerymodel.getMomStatisticKind() == 5) {
            //日-分时数据
            dateHistogramInterval = DateHistogramInterval.HOUR;
            dateFormat = "yyyy-MM-dd HH";
            //        获取索引,这个月和上个月的 deviceflow-202101,deviceflow-202012
            List<String> hourByDay = OpenApiDataVService.getTimeBetween(DateUtil.format(esQuerymodel.getCurrentBeginTime(), DateUtil.yyyyMM), DateUtil.format(esQuerymodel.getCurrentEndTime(), DateUtil.yyyyMM), DateUtil.yyyyMM, Calendar.MONTH);
            for (int i = 0; i < hourByDay.size(); i++) {
                indices += "deviceflow-" + hourByDay.get(i);
                if (i < (hourByDay.size() - 1)) {
                    indices += ",";
                }
            }
        } else {
            dateHistogramInterval = DateHistogramInterval.DAY;
            dateFormat = "yyyy-MM-dd";
            //        获取索引,这个月和上个月的 deviceflow-202101,deviceflow-202012
            List<String> hourByDay = OpenApiDataVService.getTimeBetween(DateUtil.format(esQuerymodel.getCurrentBeginTime(), DateUtil.yyyyMM), DateUtil.format(esQuerymodel.getCurrentEndTime(), DateUtil.yyyyMM), DateUtil.yyyyMM, Calendar.MONTH);
            for (int i = 0; i < hourByDay.size(); i++) {
                indices += "deviceflow-" + hourByDay.get(i);
                if (i < (hourByDay.size() - 1)) {
                    indices += ",";
                }
            }
        }
        SearchRequest searchRequest = new SearchRequest(indices);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 查询条件，可以参考官网手册
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        Assert.notEmpty(esQuerymodel.getDeviceCodeList(), "查询Es，设备编号不能为空!");
        boolQuery.must(QueryBuilders.termsQuery("deviceCode", esQuerymodel.getDeviceCodeList()));
        boolQuery.must(boolquery);

        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("createTime").format("yyyy-MM-dd HH:mm:ss");
        if (!StringUtils.isEmpty(esQuerymodel.getCurrentBeginTime())) {
            rangeQueryBuilder.gte(DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentBeginTime()));
        }
        if (!StringUtils.isEmpty(esQuerymodel.getCurrentEndTime())) {
            rangeQueryBuilder.lte(DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentEndTime()));
        }
        boolQuery.filter(rangeQueryBuilder);
        // 组装ES请求数据
//        QueryBuilder filterQuery = QueryBuilders.boolQuery().filter(QueryBuilders.scriptQuery(
//                new Script("doc.calcTime.value % 100 == 0")
//        ));
//        boolQuery.filter(filterQuery);
        searchSourceBuilder.query(boolQuery);
        String[] includeFields = new String[]{};
        String[] excludeFields = new String[]{"sourceRef"};
        searchSourceBuilder.fetchSource(includeFields, excludeFields);

        TermsAggregationBuilder groupby_deviceCode = AggregationBuilders.terms("groupby_deviceCode").field("deviceCode").size(esQuerymodel.getDeviceCodeList().size()).executionHint("map");
        DateHistogramAggregationBuilder dhab = AggregationBuilders.dateHistogram("groupCreateTime").field("createTime").format(dateFormat).dateHistogramInterval(dateHistogramInterval);
        for (String item : necessaryFieldList) {
            MaxAggregationBuilder max_createTime = AggregationBuilders.max("max_createTime" + item).field("data." + item);
            MinAggregationBuilder min_createTime = AggregationBuilders.min("min_createTime" + item).field("data." + item);

            TopHitsAggregationBuilder max_topHits = AggregationBuilders.topHits("top_score_createTime_Max" + item).sort("data." + item, SortOrder.DESC).size(1);
            TopHitsAggregationBuilder min_topHits = AggregationBuilders.topHits("top_score_createTime_Min" + item).sort("data." + item, SortOrder.ASC).size(1);
            dhab.subAggregation(max_createTime).subAggregation(max_topHits);
            dhab.subAggregation(min_createTime).subAggregation(min_topHits);
        }

        groupby_deviceCode.subAggregation(dhab);
        searchSourceBuilder.aggregation(groupby_deviceCode);
        searchSourceBuilder.trackTotalHits(true);
        //        ES 本身限制 最大只能查询一万条
        //Assert.isTrue(size <= 10000, "超过最大查询数，请缩小查询范围");
        searchSourceBuilder.from(0).size(0);  //取数大小
        searchSourceBuilder.sort("createTime", SortOrder.DESC); //倒叙
        searchRequest.source(searchSourceBuilder);
        long startTime = System.currentTimeMillis();
        Map<String, Object> sourceAsMapMin = null, sourceAsMapMax = null;
        //聚合求值
        Map<String, Map<String, Object>> deviceMap = new HashMap();
        try {
            // 查询结果
            LOGGER.info("设备数据详情ES请求数据：" + searchRequest.toString());
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            if (aggregations == null) {
                return null;
            }
            Terms groupby_deviceCodeTerms = aggregations.get("groupby_deviceCode");
            if (groupby_deviceCodeTerms == null || groupby_deviceCodeTerms.getBuckets() == null || groupby_deviceCodeTerms.getBuckets().size() <= 0) {
                LOGGER.info("设备数据详情ES请求数据为空");
                return null;
            }
            List<? extends Terms.Bucket> bucketMax = groupby_deviceCodeTerms.getBuckets();
            for (Terms.Bucket bucket : bucketMax) {
                ParsedDateHistogram groupCreateTime = bucket.getAggregations().get("groupCreateTime");
                if (groupCreateTime == null || groupCreateTime.getBuckets() == null || groupCreateTime.getBuckets().size() <= 0) {
                    continue;
                }
                String deviceCode = bucket.getKeyAsString();
                HashMap<String, Object> stringObjectHashMap = new HashMap<>();
                int i = 0;
                for (Histogram.Bucket groupCreateTimeBucket : groupCreateTime.getBuckets()) {
                    i++;
                    String datetime = String.valueOf(DateUtil.parse(groupCreateTimeBucket.getKeyAsString(), dateFormat).getTime());
                    for (String item : necessaryFieldList) {
                        TopHits top_score_createTime_min = groupCreateTimeBucket.getAggregations().get("top_score_createTime_Min" + item);
                        TopHits top_score_createTime_max = groupCreateTimeBucket.getAggregations().get("top_score_createTime_Max" + item);
                        if (top_score_createTime_min.getHits().getHits() == null || top_score_createTime_min.getHits().getTotalHits() <= 0 || top_score_createTime_max.getHits() == null || top_score_createTime_max.getHits().getTotalHits() <= 0) {
                            continue;
                        }
                        sourceAsMapMin = top_score_createTime_min.getHits().getAt(0).getSourceAsMap();
                        sourceAsMapMax = top_score_createTime_max.getHits().getAt(0).getSourceAsMap();
                        if (sourceAsMapMin == null || sourceAsMapMin.isEmpty() || sourceAsMapMax == null || sourceAsMapMax.isEmpty()) {
                            continue;
                        }
                        //add设备属性
                        HashMap data = (HashMap) sourceAsMapMax.get("data");
                        data.put("createTime", sourceAsMapMax.get("createTime"));
                        stringObjectHashMap.put(datetime, data);
//                    if(i == groupCreateTime.getBuckets().size()){
//                        //替换最后一个为最大的
//                        data = (HashMap)sourceAsMapMax.get("data");
//                        data.put("createTime",sourceAsMapMax.get("createTime"));
//                        stringObjectHashMapMax.put(datetime,data);
//                    }
                    }
                }
                //put设备
                deviceMap.put(deviceCode, stringObjectHashMap);
            }
            long sysEndTime = System.currentTimeMillis();
            LOGGER.info("查询用时:" + (sysEndTime - startTime));
            return deviceMap;
        } catch (Exception e) {
            LOGGER.error("energyStatisticsByDeviceCodeEs搜索ES数据,索引:" + indices + "异常stack_trace:{}", e.getStackTrace());
            LOGGER.error("energyStatisticsByDeviceCodeEs搜索ES数据,索引:" + indices + "异常message:{}", e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /***
     * 根据设备查询最大用量
     */
    public Map<String, Object> energyStatisticsByDeviceCodeEsMax(EsQuerymodel esQuerymodel) {
        if (ObjectUtils.isEmpty(esQuerymodel.getAttributeIdentifiers())) {
            return null;
        }
        esQuerymodel.setQueryTimeEnd(DateUtil.getYYYYMMDDDate(esQuerymodel.getCurrentEndTime()));
        Date date = new Date();
        Calendar dateEnd = Calendar.getInstance();
        dateEnd.setTime(date);
        dateEnd.set(Calendar.HOUR_OF_DAY, 23);
        dateEnd.set(Calendar.MINUTE, 59);
        dateEnd.set(Calendar.SECOND, 59);
        date = dateEnd.getTime();
        if (date.before(esQuerymodel.getCurrentEndTime())) {
            esQuerymodel.setCurrentEndTime(date);
        }
        List<String> necessaryFieldList = esQuerymodel.getAttributeIdentifiers();
        QueryBuilder boolquery = QueryBuilders.boolQuery();
        if (!CollectionUtils.isEmpty(necessaryFieldList)) {
            necessaryFieldList.forEach(item -> {
//                属性状态值 挪到了 data之下
                QueryBuilder existQuery = QueryBuilders.existsQuery("data." + item);
                ((BoolQueryBuilder) boolquery).should(existQuery);
            });
        }
        //        解决bug 【ID1003343】【设备详情】设备历史数据比设备管理平台的少一条最新的
        //index--;
        // es 6.0以上支持RestHighLevelClient
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();
        String indices = null;
        if (esQuerymodel.getMomStatisticKind() == 4 || StringUtils.isEmpty(esQuerymodel.getCurrentBeginTime())) {
            indices = "deviceflow-" + DateUtil.format(esQuerymodel.getCurrentEndTime(), "yyyy") + "*";
        } else {
            //        获取索引,这个月和上个月的 deviceflow-202101,deviceflow-202012
            indices = "deviceflow-" + DateUtil.format(esQuerymodel.getCurrentEndTime(), "yyyyMM") + "*";
        }
        SearchRequest searchRequest = new SearchRequest(indices);
        //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
        searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 查询条件，可以参考官网手册
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        Assert.notEmpty(esQuerymodel.getDeviceCodeList(), "查询Es，设备编号不能为空!");
        boolQuery.must(QueryBuilders.termsQuery("deviceCode", esQuerymodel.getDeviceCodeList()));
        boolQuery.must(boolquery);

        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("createTime").format("yyyy-MM-dd HH:mm:ss");
        if (!StringUtils.isEmpty(esQuerymodel.getCurrentBeginTime())) {
            rangeQueryBuilder.gte(DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentBeginTime()));
        }
        if (!StringUtils.isEmpty(esQuerymodel.getCurrentEndTime())) {
            rangeQueryBuilder.lte(DateUtil.getYYYYMMDDHHMMSSDate(esQuerymodel.getCurrentEndTime()));
        }
        boolQuery.filter(rangeQueryBuilder);
        // 组装ES请求数据
//        QueryBuilder filterQuery = QueryBuilders.boolQuery().filter(QueryBuilders.scriptQuery(
//                new Script("doc.calcTime.value % 100 == 0")
//        ));
//        boolQuery.filter(filterQuery);
        searchSourceBuilder.query(boolQuery);
        String[] includeFields = new String[]{};
        String[] excludeFields = new String[]{"sourceRef"};
        searchSourceBuilder.fetchSource(includeFields, excludeFields);

        TermsAggregationBuilder groupby_deviceCode = AggregationBuilders.terms("groupby_deviceCode").field("deviceCode").size(esQuerymodel.getDeviceCodeList().size()).executionHint("map");
        MaxAggregationBuilder max_createTime = AggregationBuilders.max("max_createTime").field("data." + necessaryFieldList.get(0));
        MinAggregationBuilder min_createTime = AggregationBuilders.min("min_createTime").field("data." + necessaryFieldList.get(0));
        TopHitsAggregationBuilder max_topHits = AggregationBuilders.topHits("top_score_createTime_Max").sort("data." + necessaryFieldList.get(0), SortOrder.DESC).size(1);
        groupby_deviceCode.subAggregation(max_createTime).subAggregation(max_topHits);
        searchSourceBuilder.aggregation(groupby_deviceCode);
        searchSourceBuilder.trackTotalHits(true);
        //        ES 本身限制 最大只能查询一万条
        //Assert.isTrue(size <= 10000, "超过最大查询数，请缩小查询范围");
        searchSourceBuilder.from(0).size(0);  //取数大小
        searchSourceBuilder.sort("createTime", SortOrder.DESC); //倒叙
        searchRequest.source(searchSourceBuilder);
        long startTime = System.currentTimeMillis();
        try {
            // 查询结果
            LOGGER.info("设备数据详情ES请求数据：" + searchRequest.toString());
            SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            if (aggregations == null) {
                return null;
            }
            Terms groupby_deviceCodeTerms = aggregations.get("groupby_deviceCode");
            if (groupby_deviceCodeTerms == null || groupby_deviceCodeTerms.getBuckets() == null || groupby_deviceCodeTerms.getBuckets().size() <= 0) {
                LOGGER.info("设备数据详情ES请求数据为空");
                return null;
            }
            List<? extends Terms.Bucket> bucketMax = groupby_deviceCodeTerms.getBuckets();
            Map<String, Object> map = new HashMap<>();
            Map<String, Object> sourceAsMapMin = null;
            for (Terms.Bucket bucket : bucketMax) {
                String deviceCode = bucket.getKeyAsString();
                TopHits topScoreResult = bucket.getAggregations().get("top_score_createTime_Max");
                sourceAsMapMin = topScoreResult.getHits().getAt(0).getSourceAsMap();
                if (sourceAsMapMin == null || sourceAsMapMin.isEmpty()) {
                    continue;
                }
                //add设备属性
                HashMap data = (HashMap) sourceAsMapMin.get("data");
                data.put("createTime", sourceAsMapMin.get("createTime"));
                map.put(deviceCode, data);
            }
            long sysEndTime = System.currentTimeMillis();
            LOGGER.info("查询用时:" + (sysEndTime - startTime));
            return map;
        } catch (Exception e) {
            LOGGER.error("energyStatisticsByDeviceCodeEsMax搜索ES数据,索引:" + indices + "异常stack_trace:{}", e.getStackTrace());
            LOGGER.error("energyStatisticsByDeviceCodeEsMax搜索ES数据,索引:" + indices + "异常message:{}", e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Map<String, Object> getDeviceCollectDataAggFromEs2(Map<String, Object> paramMap, String index) {
//        String index = EsCalendar.getDeviceFlowIndex(monthCount);

        String path = "http://" + eSconfig.getHost() + ":" + eSconfig.getPort() + "/" + index + "/_search?ignore_unavailable=true";
        HttpPost httpPost = new HttpPost(path);
        StringEntity se = new StringEntity(JSONObject.toJSONString(paramMap), ContentType.APPLICATION_JSON);
        httpPost.setEntity(se);
        String str = HttpClientUtil.doPost(httpPost);
        return JSONObject.parseObject(str);
    }

    @Override
    public Map<String, Object> statisticsEnergyGroupByDevice(DeviceQueryVo deviceQueryVo) {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        deviceQueryVo.setTenantId(tenantId);
        List<String> codes = deviceService.selectDeviceCodes(deviceQueryVo);
        if (codes.isEmpty()) {
            return new LinkedHashMap();
        }
        String queryStr = EsQueryString.queryPeriodTimeFirstItemGroupByDeviceCode;
//        queryStr = queryStr.replaceAll("@projectId", getProjectId());
//        只取 yyyy-MM-dd
        String queryTimeEnd = deviceQueryVo.getQueryTimeEnd().substring(0, 10);
        queryStr = queryStr.replaceAll("@getQueryTimeStart", deviceQueryVo.getQueryTimeStart().substring(0, 10));
        queryStr = queryStr.replaceAll("@getQueryTimeEnd", queryTimeEnd);
//        if (!ObjectUtils.isEmpty(deviceQueryVo.getHasAttrs())) {
//            queryStr = queryStr.replaceAll("@getHasAttrs0", deviceQueryVo.getHasAttrs().get(0));
//        }
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@shouldTemplate\"", shouldArray.toJSONString());
        LOGGER.info("the queryStr : {}", queryStr);
//        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "asc"));
//        JSONObject paramMap2 = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        JSONObject paramMap2 = JSONObject.parseObject(queryStr);
//        Map<String, Double> r1 = getDeviceCode2ValueMap(deviceQueryVo, paramMap);
        Map<String, Double> r2 = getDeviceCode2ValueMapBingTu(deviceQueryVo, paramMap2);
        BigDecimal total = BigDecimal.ZERO;
        for (Entry<String, Double> sd : r2.entrySet()) {
            if (sd.getValue() != null) {
                BigDecimal v = getBigDecimal(sd.getValue());
                r2.put(sd.getKey(), v.doubleValue());
                total = total.add(v);
            }
        }
//        以上结果是还没有算今天的,如果查询时间截止到今天，需要补充上今天的计算值
        if (queryTimeEnd.compareTo(DateUtil.getYYYYMMDDDate(new Date())) >= 0) {
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            deviceQueryVo.setTimeType(1);
            cal.add(Calendar.DATE, -1);
            deviceQueryVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
            cal.add(Calendar.DATE, 2);
            deviceQueryVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
            List<Map> todayStatistics = getEnergyGroupByDevice(deviceQueryVo, deviceQueryVo.getQueryTimeStart().substring(0, 10), deviceQueryVo.getQueryTimeEnd().substring(0, 10));
            for (Map todayStatistic : todayStatistics) {
                String deviceCode = (String) todayStatistic.get("deviceCode");
                BigDecimal today = getBigDecimal(todayStatistic.get("calculateValue"));
                BigDecimal result = getBigDecimal(r2.get(deviceCode));
                result = result != null ? result : BigDecimal.ZERO;
                if (today != null) {
                    r2.put(deviceCode, today.add(result).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                }
            }
        }
        if (!ObjectUtils.isEmpty(r2)) {
//        code 替换为设备名称
            QueryWrapper qw = new QueryWrapper();
            qw.select("name", "code");
            qw.in("code", codes);
            qw.eq("delete_state", 1);
            List<Device> devices = deviceService.list(qw);
            for (Device device : devices) {
                if (r2.get(device.getName()) == null && r2.get(device.getCode()) != null) {
                    r2.put(device.getName(), r2.get(device.getCode()));
                    r2.remove(device.getCode());
                }
            }
        }
        Map result = new LinkedHashMap();
        result.put("result", r2);
        result.put("total", total.doubleValue());
        return result;
    }

    private Map getDeviceCode2ValueMap(DeviceQueryVo deviceQueryVo, JSONObject paramMap) {
        Map result = new LinkedHashMap();
        int monthCount = 2;
        if (Objects.equals(deviceQueryVo.getTimeType(), 2)) {
//            13个月
            monthCount = 13;
        } else if (Objects.equals(deviceQueryVo.getTimeType(), 3)) {
//            10年 ,因为年份太大了，会包索引不存在，2020-10之前的索引均不存在
            monthCount = 15;
        }
        Map r1 = getDeviceCollectDataAggFromEs2(paramMap, EsCalendar.getDeviceFlowIndex(monthCount));
//        Map data = (Map) r1.get("data");
        Map aggregations = (Map) r1.get("aggregations");
        Map deviceCodeAgg = (Map) aggregations.get("deviceCodeAgg");
        List<Map> buckets = (List) deviceCodeAgg.get("buckets");
        for (Map bucket : buckets) {
            String deviceCode = (String) bucket.get("key");
            Map top1 = (Map) bucket.get("top1");
            Map hits0 = (Map) top1.get("hits");
            List<Map> hits2 = (List) hits0.get("hits");
            Map source = (Map) hits2.get(0).get("_source");
            Map data1 = (Map) source.get("data");
            Object obj = data1.get(deviceQueryVo.getHasAttrs().get(0));
            BigDecimal value = getBigDecimal(obj);
            if (value != null) {
                result.put(deviceCode, value);
            }
        }
        return result;
    }

    private Map getDeviceCode2ValueMapBingTu(DeviceQueryVo deviceQueryVo, JSONObject paramMap) {
        Map result = new LinkedHashMap();
        Map r1 = getDeviceCollectDataAggFromEs2(paramMap, DEVICE_ENERGY_COLLECTION_INDEX);
        if (ObjectUtils.isEmpty(r1)) {
            return result;
        }
        Map aggregations = (Map) r1.get("aggregations");
        if (ObjectUtils.isEmpty(aggregations)) {
            return result;
        }
        Map deviceCodeAgg = (Map) aggregations.get("deviceCodeAgg");
        if (ObjectUtils.isEmpty(deviceCodeAgg)) {
            return result;
        }
        List<Map> buckets = (List) deviceCodeAgg.get("buckets");
        if (ObjectUtils.isEmpty(buckets)) {
            return result;
        }
        for (Map bucket : buckets) {
            String deviceCode = (String) bucket.get("key");
            Map top1 = (Map) bucket.get("top1");
            BigDecimal value = getBigDecimal(top1.get("value"));
            if (value != null) {
                result.put(deviceCode, value.setScale(2, BigDecimal.ROUND_HALF_UP));
            }
        }
        return result;
    }

    private Map getDeviceCode2ValueMap2(DeviceQueryVo deviceQueryVo, JSONObject paramMap) {
        Map result = new LinkedHashMap();
        int monthCount = 2;
        if (Objects.equals(deviceQueryVo.getTimeType(), 2)) {
//            13个月
            monthCount = 13;
        } else if (Objects.equals(deviceQueryVo.getTimeType(), 3)) {
//            10年 ,因为年份太大了，会包索引不存在，2020-10之前的索引均不存在
            monthCount = 15;
        }
        Map r1 = getDeviceCollectDataAggFromEs2(paramMap, EsCalendar.getDeviceFlowIndex(monthCount));
        Map aggregations = (Map) r1.get("aggregations");
        if (aggregations == null) {
            return result;
        }
        Map deviceCodeAgg = (Map) aggregations.get("deviceCodeAgg");
        List<Map> buckets = (List) deviceCodeAgg.get("buckets");
        for (Map bucket : buckets) {
            Map deviceEnergyMapByTime = new LinkedHashMap();
            String deviceCode = (String) bucket.get("key");
            Map aggCollectTimeGroup = (Map) bucket.get("aggCollectTimeGroup");
            if (aggCollectTimeGroup != null) {
                Collection<Map> aggCollectTimeGroupBuckets = (Collection) aggCollectTimeGroup.get("buckets");
                for (Map aggCollectTimeGroupBucket : aggCollectTimeGroupBuckets) {
                    String dateStr = (String) aggCollectTimeGroupBucket.get("key");
                    Map top1 = (Map) aggCollectTimeGroupBucket.get("top1");
                    BigDecimal value = getBigDecimal(top1.get("value"));
                    deviceEnergyMapByTime.put(dateStr, value);

                }
            }
            result.put(deviceCode, deviceEnergyMapByTime);
        }
        return result;
    }


    private Map getDeviceCode2ValueMap3(DeviceQueryVo deviceQueryVo, JSONObject paramMap) {
        Map result = new LinkedHashMap();
        Map r1 = getDeviceCollectDataAggFromEs2(paramMap, DEVICE_ENERGY_COLLECTION_INDEX);
        Map aggregations = (Map) r1.get("aggregations");
        if (aggregations == null) {
            return result;
        }
        Map deviceCodeAgg = (Map) aggregations.get("deviceCodeAgg");
        List<Map> buckets = (List) deviceCodeAgg.get("buckets");
        for (Map bucket : buckets) {
            Map deviceEnergyMapByTime = new LinkedHashMap();
            String deviceCode = (String) bucket.get("key");
            Map aggCollectTimeGroup = (Map) bucket.get("aggCollectTimeGroup");
            if (aggCollectTimeGroup != null) {
                Collection<Map> aggCollectTimeGroupBuckets = (Collection) aggCollectTimeGroup.get("buckets");
                for (Map aggCollectTimeGroupBucket : aggCollectTimeGroupBuckets) {
                    String dateStr = (String) aggCollectTimeGroupBucket.get("key");
                    Map top1 = (Map) aggCollectTimeGroupBucket.get("top1");
                    BigDecimal value = getBigDecimal(top1.get("value"));
                    deviceEnergyMapByTime.put(dateStr, value);

                }
            }
            result.put(deviceCode, deviceEnergyMapByTime);
        }
        return result;
    }

    private BigDecimal getBigDecimal(Object obj) {
        BigDecimal value = null;
        if (obj instanceof Integer) {
            value = BigDecimal.valueOf((Integer) obj);
        } else if (obj instanceof Double) {
            value = BigDecimal.valueOf((Double) obj);
        } else if (obj instanceof BigDecimal) {
            value = (BigDecimal) obj;
        } else if (obj instanceof CharSequence) {
            value = new BigDecimal(obj.toString());
        }
        return value;
    }

    @Override
    public HashMap<String, BigDecimal> statisticsEnergyGroupByDeviceTopN(DeviceQueryVo deviceQueryVo) {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        deviceQueryVo.setTenantId(tenantId);
        List<String> codes = deviceService.selectDeviceCodes(deviceQueryVo);
        if (codes.isEmpty()) {
            return new LinkedHashMap();
        }
        String queryStr = EsQueryString.queryPeriodTimeFirstItemGroupByDeviceCodeTop;
        queryStr = queryStr.replaceAll("@projectId", getProjectId());
        if (!ObjectUtils.isEmpty(deviceQueryVo.getHasAttrs())) {
            queryStr = queryStr.replaceAll("@getHasAttrs0", deviceQueryVo.getHasAttrs().get(0));
        }
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@shouldTemplate\"", shouldArray.toJSONString());
        LOGGER.info("the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        Map<String, Comparable> r1 = getDeviceCode2ValueMap(deviceQueryVo, paramMap);

//        code 替换为设备名称
        QueryWrapper qw = new QueryWrapper();
        qw.select("name", "code");
        qw.in("code", codes);
        qw.eq("delete_state", 1);
        List<Device> devices = deviceService.list(qw);
        for (Device device : devices) {
            if (r1.get(device.getName()) == null && r1.get(device.getCode()) != null) {
                r1.put(device.getName(), r1.get(device.getCode()));
                r1.remove(device.getCode());
            }
        }
// map 根据值排序
        HashMap<String, BigDecimal> finalOut = new LinkedHashMap<>();
        r1.entrySet().stream().sorted((p1, p2) -> p2.getValue().compareTo(p1.getValue())).collect(Collectors.toList()).forEach(ele -> finalOut.put(ele.getKey(), getBigDecimal(ele.getValue())));

        return finalOut;
    }

    /***
     * 定时采集设备能耗使用量 。每天0点过10分
     */
    @Scheduled(cron = "0 10 1 * * ?")
    @Override
    public boolean collectStatisticsEnergyGroupByDeviceByTimeSchedule() {
//        if (!openReceiving) {
//            LOGGER.info("openReceiving 为false");
//            return false;
//        }
        LOGGER.info("collectStatisticsEnergyGroupByDeviceByTimeSchedule 定时任务开始执行");
        RLock redissonLock = redisson.getLock("collectStatisticsEnergyGroupByDeviceByTimeSchedule_lock");
        if (redissonLock.tryLock()) {
            try {
                DeviceQueryVo deviceQueryVo = new DeviceQueryVo();
                deviceQueryVo.setTimeType(1);
                Calendar cal = Calendar.getInstance();
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                Date queryTimeEnd = cal.getTime();
                deviceQueryVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
                cal.add(Calendar.DATE, -2);
                Date queryTimeStart = cal.getTime();
                deviceQueryVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
                deviceQueryVo.setTenantId(sikeTenantId);
//                采集电的
                deviceQueryVo.setHasAttrs(Arrays.asList(sikeElectricityPropCode));
                collectEnergyGroupByDevice(deviceQueryVo, DateUtil.getYYYYMMDDDate(queryTimeStart), DateUtil.getYYYYMMDDDate(queryTimeEnd));
//                采集水的
                deviceQueryVo.setHasAttrs(Arrays.asList(sikeWaterPropCode));
                collectEnergyGroupByDevice(deviceQueryVo, DateUtil.getYYYYMMDDDate(queryTimeStart), DateUtil.getYYYYMMDDDate(queryTimeEnd));
            } catch (Exception e) {
                log.error("collectStatisticsEnergyGroupByDeviceByTimeSchedule error:", e);
                e.printStackTrace();
                return false;
            } finally {
                redissonLock.unlock();
            }
            LOGGER.info("collectStatisticsEnergyGroupByDeviceByTimeSchedule 定时任务结束");
        }
        return true;
    }

    @Override
    public Map collectStatisticsEnergyGroupByDeviceByTime(DeviceQueryVo deviceQueryVo) {
        List list = new ArrayList();
        String timeEndStr = deviceQueryVo.getQueryTimeEnd();
        String timeStartStr = deviceQueryVo.getQueryTimeStart();
        Date timeStart = DateUtil.getDateFromYYYYMMDDHHmmss(timeStartStr);
        Date timeEnd = DateUtil.getDateFromYYYYMMDDHHmmss(timeEndStr);
        Calendar cal = Calendar.getInstance();
        cal.setTime(timeEnd);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
//        String timeEndBefore = DateUtil.getYYYYMMDDDate(new Date(timeEnd.getTime() - 24 * 60 * 60 * 1000));
//        单次循环只统计一天的数据，插入es,收集时间范围前闭后开区间
        while (timeStart.compareTo(cal.getTime()) < 0) {
            deviceQueryVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
            Date queryTimeStart = new Date(cal.getTimeInMillis() - 2 * 24 * 60 * 60 * 1000);
            deviceQueryVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(queryTimeStart));
            list.addAll(collectEnergyGroupByDevice(deviceQueryVo, DateUtil.getYYYYMMDDDate(queryTimeStart), DateUtil.getYYYYMMDDDate(cal.getTime())));
            LOGGER.info("循环采集统计数据：" + JSONObject.toJSONString(deviceQueryVo));
            cal.add(Calendar.DATE, -1);
        }
        Map result = new LinkedHashMap();
        result.put("添加或修改设备数据", list);
        return result;
    }

    /**
     * 采集设备的分组统计的能耗. 计算范围 [collectTimeStart,collectTimeEnd) 前闭后开区间
     *
     * @param deviceQueryVo    查询实体
     * @param collectTimeStart 采集开始时间 格式 yyyy-MM-dd
     * @param collectTimeEnd   采集结束时间 格式 yyyy-MM-dd
     * @return
     */
    private List collectEnergyGroupByDevice(DeviceQueryVo deviceQueryVo, String collectTimeStart, String collectTimeEnd) {
        List<Map> list = getEnergyGroupByDevice(deviceQueryVo, collectTimeStart, collectTimeEnd);
        for (Map map : list) {
            addItemToEs(map, DEVICE_ENERGY_COLLECTION_INDEX, map.get("deviceCode") + "@" + map.get("calculateTime"));
        }
        return list;
    }

    private List getEnergyGroupByDevice(DeviceQueryVo deviceQueryVo, String collectTimeStart, String collectTimeEnd) {
        String timeEndStr = deviceQueryVo.getQueryTimeEnd();
        String timeStartStr = deviceQueryVo.getQueryTimeStart();
        if (deviceQueryVo.getTenantId() == null) {
            deviceQueryVo.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        }
        List<String> codes = deviceService.selectDeviceCodes(deviceQueryVo);
        if (codes.isEmpty()) {
            return new ArrayList();
        }
        String queryStr = EsQueryString.queryPeriodTimeMaxGroupByDeviceByTime;
        LinkappTenant tenant = linkappTenantService.getOneById(deviceQueryVo.getTenantId());
        queryStr = queryStr.replaceAll("@projectId", tenant.getProjectId());
        queryStr = queryStr.replaceAll("@getQueryTimeStart", timeStartStr);
        queryStr = queryStr.replaceAll("@getQueryTimeEnd", timeEndStr);
        if (!ObjectUtils.isEmpty(deviceQueryVo.getHasAttrs())) {
            queryStr = queryStr.replaceAll("@getHasAttrs0", deviceQueryVo.getHasAttrs().get(0));
        }
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@shouldTemplate\"", shouldArray.toJSONString());
        LOGGER.info("the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        Map<String, LinkedHashMap> r1 = getDeviceCode2ValueMap2(deviceQueryVo, paramMap);
        if (ObjectUtils.isEmpty(r1)) {
            return new ArrayList();
        }
        List r3 = new ArrayList();
        for (Entry<String, LinkedHashMap> e : r1.entrySet()) {
            LinkedHashMap<String, Object> val = e.getValue();
            val = crossSub(val, deviceQueryVo);
            Map r2 = new LinkedHashMap(8);
            val.forEach((k, v) -> {
//                只收集一天的插入
                if (k.compareTo(collectTimeStart) >= 0 && k.compareTo(collectTimeEnd) < 0) {
                    Date date = new Date();
                    r2.put("deviceCode", e.getKey());
                    r2.put("createTime", DateUtil.getYYYYMMDDHHMMSSDate(date));
                    r2.put("calculateTime", k);
                    r2.put("calculateValue", getBigDecimal(v).doubleValue());
                    r2.put("timestamp", date.getTime());
                    r3.add(r2);
                }
            });
        }
        return r3;
    }

    public Map<String, Object> addItemToEs(Map<String, Object> paramMap, String index, String id) {
        String path = "http://" + eSconfig.getHost() + ":" + eSconfig.getPort() + "/" + index + "/_doc/" + id;
        HttpPost httpPost = new HttpPost(path);
        StringEntity se = new StringEntity(JSONObject.toJSONString(paramMap), ContentType.APPLICATION_JSON);
        httpPost.setEntity(se);
        String str = HttpClientUtil.doPost(httpPost);
        if (str.contains("error")) {
            LOGGER.warn("insert es error: {}", str);
        }
        LOGGER.info("addItemToEs采集数据插入es:{},id:{}", JSONObject.toJSONString(paramMap), id);
        return JSONObject.parseObject(str);
    }

    @Override
    public Map<String, LinkedHashMap> statisticsEnergyGroupByDeviceByTime2(DeviceQueryVo deviceQueryVo) {
        Calendar cal = Calendar.getInstance();
        deviceQueryVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
        String queryStr = null;
        switch (deviceQueryVo.getTimeType()) {
            case 1:
                Map<String, LinkedHashMap> r = statisticsEnergyGroupByDeviceByTime(deviceQueryVo);
//                将今天的去除掉
                String today = DateUtil.getYYYYMMDDDate(new Date());
                for (Entry<String, LinkedHashMap> stringLinkedHashMapEntry : r.entrySet()) {
                    LinkedHashMap<String, Double> val = stringLinkedHashMapEntry.getValue();
                    LinkedHashMap<String, Double> val2 = new LinkedHashMap<>();
                    for (Entry<String, Double> stringDoubleEntry : val.entrySet()) {
                        if (stringDoubleEntry.getKey().compareTo(today) < 0) {
                            val2.put(stringDoubleEntry.getKey(), stringDoubleEntry.getValue());
                        }
                    }
                    r.put(stringLinkedHashMapEntry.getKey(), val2);
                }
                return r;
            case 2:
                cal.add(Calendar.MONTH, -12);
                queryStr = EsQueryString.collectPeriodTimeMaxGroupByDeviceByTime2;
                break;
            case 3:
            default:
                cal.add(Calendar.YEAR, -10);
                queryStr = EsQueryString.collectPeriodTimeMaxGroupByDeviceByTime3;
                break;
        }
        deviceQueryVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        deviceQueryVo.setTenantId(tenantId);
        List<String> codes = deviceService.selectDeviceCodes(deviceQueryVo);
        if (codes.isEmpty()) {
            return new LinkedHashMap();
        }
        queryStr = queryStr.replaceAll("@projectId", getProjectId());
        queryStr = queryStr.replaceAll("@getQueryTimeStart", deviceQueryVo.getQueryTimeStart());
        queryStr = queryStr.replaceAll("@getQueryTimeEnd", deviceQueryVo.getQueryTimeEnd());
        if (!ObjectUtils.isEmpty(deviceQueryVo.getHasAttrs())) {
            queryStr = queryStr.replaceAll("@getHasAttrs0", deviceQueryVo.getHasAttrs().get(0));
        }
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@shouldTemplate\"", shouldArray.toJSONString());
        LOGGER.info("the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        Map<String, LinkedHashMap> r1 = getDeviceCode2ValueMap3(deviceQueryVo, paramMap);
        for (Entry<String, LinkedHashMap> e : r1.entrySet()) {
            LinkedHashMap val = e.getValue();
            val = fillValue2Key(val, deviceQueryVo);
            r1.put(e.getKey(), val);
        }
//        code 替换为设备名称
        QueryWrapper qw = new QueryWrapper();
        qw.select("name", "code");
        qw.in("code", codes);
        qw.eq("delete_state", 1);
        List<Device> devices = deviceService.list(qw);
        for (Device device : devices) {
            if (r1.get(device.getName()) == null && r1.get(device.getCode()) != null) {
                r1.put(device.getName(), r1.get(device.getCode()));
                r1.remove(device.getCode());
            }
        }
        return r1;
    }

    @Override
    public IPage<AiCameraVo> queryAiCamera(RequestModel<AiCameraVo> aiCameraVoRequestModel) {
        AiCameraVo aiCameraVo = aiCameraVoRequestModel.getCustomQueryParams();
        Page page = aiCameraVoRequestModel.getPage();
        String queryStr = EsQueryString.queryAiCamera;

        queryStr = queryStr.replaceAll("@from", String.valueOf((page.getCurrent() - 1) * page.getSize()));
        queryStr = queryStr.replaceAll("@size", String.valueOf(page.getSize()));
        queryStr = queryStr.replaceAll("@projectId", getProjectId());
        if (ObjectUtils.isEmpty(aiCameraVo.getQueryTimeStart())) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MONTH, -6);
            aiCameraVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(calendar.getTime()));
        }
        if (ObjectUtils.isEmpty(aiCameraVo.getQueryTimeEnd())) {
            aiCameraVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(new Date()));
        }
        queryStr = queryStr.replaceAll("@createTime_from", aiCameraVo.getQueryTimeStart());
        queryStr = queryStr.replaceAll("@createTime_to", aiCameraVo.getQueryTimeEnd());
        queryStr = queryStr.replaceAll("@order", "desc");
//        不区分租户，全局
        List<String> codes = deviceConfigUtil.getAiCameraDeviceUnitList(null);
        JSONArray mustArray = new JSONArray();
        if (!ObjectUtils.isEmpty(aiCameraVo.getRuleName())) {
            Integer ruleType = null;
            for (EsConstantEnum.AiCameraRuleType value : EsConstantEnum.AiCameraRuleType.values()) {
                if (value.getRuleName().equals(aiCameraVo.getRuleName())) {
                    ruleType = value.getRuleType();
                    break;
                }
            }
            if (ruleType == null) {
                return aiCameraVoRequestModel.getPage();
            }
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("data.RuleType", ruleType);
            j.put("term", j2);
            mustArray.add(j);
        }
        if (!ObjectUtils.isEmpty(aiCameraVo.getDeviceCode())) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("data.ChnName.keyword", aiCameraVo.getDeviceCode());
            j.put("term", j2);
            mustArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@must\"", mustArray.toJSONString());
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceUnitCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@should\"", shouldArray.toJSONString());
        LOGGER.info("查询ES对应the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        Map<String, Object> r2 = getDeviceCollectDataAggFromEs2(paramMap, EsCalendar.getDeviceFlowIndex(aiCameraVo.getQueryTimeStart(), aiCameraVo.getQueryTimeEnd()));

        return getAiCameraVoFromBucket(aiCameraVoRequestModel.getPage(), r2);
    }

    @Override
    public IPage<AiCameraVo> queryAiAlarm(RequestModel<AiCameraVo> aiCameraVoRequestModel) {
        AiCameraVo aiCameraVo = aiCameraVoRequestModel.getCustomQueryParams();
        Page page = aiCameraVoRequestModel.getPage();
        String queryStr = EsQueryString.queryAiCamera;


        // 通过城建项目编号查询施工云项目租户ID
        QueryWrapper<LinkappTenant> qw = new QueryWrapper<>();
        qw.eq("project_code_", aiCameraVo.getProjectCode());
        String projectId = linkappTenantMapper.selectOne(qw).getProjectId();

        queryStr = queryStr.replaceAll("@from", String.valueOf((page.getCurrent() - 1) * page.getSize()));
        queryStr = queryStr.replaceAll("@size", String.valueOf(page.getSize()));
        queryStr = queryStr.replaceAll("@projectId", projectId);
        if (ObjectUtils.isEmpty(aiCameraVo.getQueryTimeStart())) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MONTH, -6);
            aiCameraVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(calendar.getTime()));
        }
        if (ObjectUtils.isEmpty(aiCameraVo.getQueryTimeEnd())) {
            aiCameraVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(new Date()));
        }
        queryStr = queryStr.replaceAll("@createTime_from", aiCameraVo.getQueryTimeStart());
        queryStr = queryStr.replaceAll("@createTime_to", aiCameraVo.getQueryTimeEnd());
        queryStr = queryStr.replaceAll("@order", "desc");
//        不区分租户，全局
        List<String> codes = deviceConfigUtil.getAiCameraDeviceUnitList(null);
        JSONArray mustArray = new JSONArray();
        if (!ObjectUtils.isEmpty(aiCameraVo.getRuleName())) {
            Integer ruleType = null;
            for (EsConstantEnum.AiCameraRuleType value : EsConstantEnum.AiCameraRuleType.values()) {
                if (value.getRuleName().equals(aiCameraVo.getRuleName())) {
                    ruleType = value.getRuleType();
                    break;
                }
            }
            if (ruleType == null) {
                return aiCameraVoRequestModel.getPage();
            }
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("data.RuleType", ruleType);
            j.put("term", j2);
            mustArray.add(j);
        }
        if (!ObjectUtils.isEmpty(aiCameraVo.getDeviceCode())) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("data.ChnName.keyword", aiCameraVo.getDeviceCode());
            j.put("term", j2);
            mustArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@must\"", mustArray.toJSONString());
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceUnitCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@should\"", shouldArray.toJSONString());
        LOGGER.info("查询ES对应the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        Map<String, Object> r2 = getDeviceCollectDataAggFromEs2(paramMap, EsCalendar.getDeviceFlowIndex(aiCameraVo.getQueryTimeStart(), aiCameraVo.getQueryTimeEnd()));

        return getAiCameraVoFromBucket(aiCameraVoRequestModel.getPage(), r2);
    }

    private IPage<AiCameraVo> getAiCameraVoFromBucket(Page page, Map<String, Object> r1) {
        ArrayList<AiCameraVo> arrayList = new ArrayList<>();
        Map hits = (Map) r1.get("hits");
        if (hits != null) {
            Integer total = (Integer) hits.get("total");
            if (total != null) {
                page.setTotal(total);
            }
            List<Map> hitsList = (List) hits.get("hits");
            if (hitsList != null && !hits.isEmpty()) {
                for (Map map : hitsList) {
                    AiCameraVo aiCameraVo = new AiCameraVo();
                    Map source = (Map) map.get("_source");
                    if (source != null) {
//                        Map data = (Map) source.get("data");
//                        JSONObject data = JSON.parseObject((String) source.get("message"));
                        if (null != source.get("data")) {
                            JSONObject data = (JSONObject) source.get("data");
                            aiCameraVo.setRuleName(data.getString("RuleName"));
                            aiCameraVo.setRuleId(data.getString("RuleId"));
                            aiCameraVo.setRuleType(data.getString("RuleType"));
                            aiCameraVo.setImage1(data.getString("image1"));
                            aiCameraVo.setImage2(data.getString("image2"));
                            aiCameraVo.setObjType(data.getString("ObjType"));
                            aiCameraVo.setAge(data.get("age") == null ? null : Integer.valueOf(data.get("age").toString()));
                            aiCameraVo.setTime(data.getString("Time"));
                            aiCameraVo.setChnName(data.getString("ChnName"));
                            aiCameraVo.setDeviceCode(data.getString("ChnName"));
                        }
                        aiCameraVo.setCreateTime(String.valueOf(source.get("createTime")));
                        aiCameraVo.setDeviceTypeName(String.valueOf(source.get("deviceTypeName")));
                        aiCameraVo.setDeviceUnitCode(String.valueOf(source.get("deviceUnitCode")));
                    }
                    arrayList.add(aiCameraVo);
                }
            }
        }
//        子设备code
        Set<String> deviceCodes = arrayList.stream().map(AiCameraVo::getChnName).collect(Collectors.toSet());
        if (!deviceCodes.isEmpty()) {
            LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.select(Device::getCode, Device::getName);
            lambdaQueryWrapper.in(Device::getCode, deviceCodes);
            List<Device> devices = deviceService.list(lambdaQueryWrapper);
            if (org.apache.commons.lang3.ObjectUtils.isNotEmpty(devices)) {
                Map<String, String> code2name = new HashMap<>();
                devices.forEach(e -> {
                    code2name.put(e.getCode(), e.getName());
                });
                for (AiCameraVo aiCameraVo : arrayList) {
                    aiCameraVo.setDeviceName(code2name.get(aiCameraVo.getDeviceCode()));
                }
            }
        }
        page.setRecords(arrayList);
        return page;
    }

    @Override
    public Map<String, LinkedHashMap> statisticsEnergyGroupByDeviceByTime(DeviceQueryVo deviceQueryVo) {
        Calendar cal = Calendar.getInstance();
        deviceQueryVo.setQueryTimeEnd(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
        String queryStr = null;
        switch (deviceQueryVo.getTimeType()) {
            case 2:
                cal.add(Calendar.YEAR, -1);
                queryStr = EsQueryString.queryPeriodTimeMaxGroupByDeviceByTime2;
                break;
            case 3:
                cal.add(Calendar.YEAR, -10);
                queryStr = EsQueryString.queryPeriodTimeMaxGroupByDeviceByTime3;
                break;
            case 1:
            default:
                cal.add(Calendar.MONTH, -1);
                queryStr = EsQueryString.queryPeriodTimeMaxGroupByDeviceByTime;
                break;
        }
        deviceQueryVo.setQueryTimeStart(DateUtil.getYYYYMMDDHHMMSSDate(cal.getTime()));
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        deviceQueryVo.setTenantId(tenantId);
        List<String> codes = deviceService.selectDeviceCodes(deviceQueryVo);
        if (codes.isEmpty()) {
            return new LinkedHashMap();
        }
        queryStr = queryStr.replaceAll("@projectId", getProjectId());
        queryStr = queryStr.replaceAll("@getQueryTimeStart", deviceQueryVo.getQueryTimeStart());
        queryStr = queryStr.replaceAll("@getQueryTimeEnd", deviceQueryVo.getQueryTimeEnd());
        if (!ObjectUtils.isEmpty(deviceQueryVo.getHasAttrs())) {
            queryStr = queryStr.replaceAll("@getHasAttrs0", deviceQueryVo.getHasAttrs().get(0));
        }
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@shouldTemplate\"", shouldArray.toJSONString());
        LOGGER.info("the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr.replaceAll("#orderVal", "desc"));
        Map<String, LinkedHashMap> r1 = getDeviceCode2ValueMap2(deviceQueryVo, paramMap);
        for (Entry<String, LinkedHashMap> e : r1.entrySet()) {
            LinkedHashMap val = e.getValue();
            val = crossSub(val, deviceQueryVo);
            r1.put(e.getKey(), val);
        }
//        code 替换为设备名称
        QueryWrapper qw = new QueryWrapper();
        qw.select("name", "code");
        qw.in("code", codes);
        qw.eq("delete_state", 1);
        List<Device> devices = deviceService.list(qw);
        for (Device device : devices) {
            if (r1.get(device.getName()) == null && r1.get(device.getCode()) != null) {
                r1.put(device.getName(), r1.get(device.getCode()));
                r1.remove(device.getCode());
            }
        }
        return r1;
    }

    /**
     * 错位相减
     * 比如日的就是，计算日的统计值 = 计算日最大值 - 计算日前一天的最大值
     *
     * @param map
     * @param deviceQueryVo
     */
    private LinkedHashMap crossSub(LinkedHashMap map, DeviceQueryVo deviceQueryVo) {
        LinkedHashMap r = new LinkedHashMap();
        Calendar startTime = Calendar.getInstance();
        startTime.setTime(DateUtil.getDateFromYYYYMMDDHHmmss(deviceQueryVo.getQueryTimeStart()));
        String startTimeStr = null;
        String calculateTimeBefore1Str = null;
        Calendar end = Calendar.getInstance();
        if (deviceQueryVo.getTimeType().equals(1)) {
            end.set(Calendar.HOUR, 0);
            while (startTime.getTimeInMillis() < end.getTimeInMillis()) {
                calculateTimeBefore1Str = DateUtil.getYYYYMMDDDate(startTime.getTime());
                startTime.add(Calendar.DATE, 1);
                startTimeStr = DateUtil.getYYYYMMDDDate(startTime.getTime());
                BigDecimal currVal = (BigDecimal) map.get(startTimeStr);
                BigDecimal currValBefore1 = (BigDecimal) map.get(calculateTimeBefore1Str);
                r.put(startTimeStr, BigDecimal.ZERO);
                if (startTimeStr != null && calculateTimeBefore1Str != null && currVal != null && currValBefore1 != null) {
                    r.put(startTimeStr, currVal.subtract(currValBefore1));
                }
            }
        } else if (deviceQueryVo.getTimeType().equals(2)) {
            end.set(Calendar.HOUR, 0);
            while (startTime.getTimeInMillis() < end.getTimeInMillis()) {
                calculateTimeBefore1Str = DateUtil.getDateFromFormatStr(startTime.getTime(), "yyyy-MM");
                startTime.add(Calendar.MONTH, 1);
                startTimeStr = DateUtil.getDateFromFormatStr(startTime.getTime(), "yyyy-MM");
                BigDecimal currVal = (BigDecimal) map.get(startTimeStr);
                BigDecimal currValBefore1 = (BigDecimal) map.get(calculateTimeBefore1Str);
                r.put(startTimeStr, BigDecimal.ZERO);
                if (startTimeStr != null && calculateTimeBefore1Str != null && currVal != null && currValBefore1 != null) {
                    r.put(startTimeStr, currVal.subtract(currValBefore1));
                }
            }
        } else if (deviceQueryVo.getTimeType().equals(3)) {
            end.set(Calendar.HOUR, 0);
            while (startTime.getTimeInMillis() < end.getTimeInMillis()) {
                calculateTimeBefore1Str = DateUtil.getDateFromFormatStr(startTime.getTime(), "yyyy");
                startTime.add(Calendar.YEAR, 1);
                startTimeStr = DateUtil.getDateFromFormatStr(startTime.getTime(), "yyyy");
                BigDecimal currVal = (BigDecimal) map.get(startTimeStr);
                BigDecimal currValBefore1 = (BigDecimal) map.get(calculateTimeBefore1Str);
                r.put(startTimeStr, BigDecimal.ZERO);
                if (startTimeStr != null && calculateTimeBefore1Str != null && currVal != null && currValBefore1 != null) {
                    r.put(startTimeStr, currVal.subtract(currValBefore1));
                }
            }
        }
        return r;
    }


    /**
     * 填充值
     *
     * @param map
     * @param deviceQueryVo
     */
    private LinkedHashMap fillValue2Key(LinkedHashMap map, DeviceQueryVo deviceQueryVo) {
        LinkedHashMap r = new LinkedHashMap();
        Calendar startTime = Calendar.getInstance();
        startTime.setTime(DateUtil.getDateFromYYYYMMDDHHmmss(deviceQueryVo.getQueryTimeStart()));
        String startTimeStr = null;
        Calendar end = Calendar.getInstance();
        if (deviceQueryVo.getTimeType().equals(2)) {
            end.set(Calendar.HOUR, 0);
            while (startTime.getTimeInMillis() < end.getTimeInMillis()) {
                startTime.add(Calendar.MONTH, 1);
                startTimeStr = DateUtil.getDateFromFormatStr(startTime.getTime(), "yyyy-MM");
                BigDecimal currVal = (BigDecimal) map.get(startTimeStr);
                r.put(startTimeStr, BigDecimal.ZERO);
                if (currVal != null) {
                    r.put(startTimeStr, currVal);
                }
            }
        } else if (deviceQueryVo.getTimeType().equals(3)) {
            end.set(Calendar.HOUR, 0);
            while (startTime.getTimeInMillis() < end.getTimeInMillis()) {
                startTime.add(Calendar.YEAR, 1);
                startTimeStr = DateUtil.getDateFromFormatStr(startTime.getTime(), "yyyy");
                BigDecimal currVal = (BigDecimal) map.get(startTimeStr);
                r.put(startTimeStr, BigDecimal.ZERO);
                if (currVal != null) {
                    r.put(startTimeStr, currVal);
                }
            }
        }
        return r;
    }


    /**
     * 求取时间段内某设备属性的平均值 （如施工云求扬尘设备 每日pm10的每日平均值）
     *
     * @param deviceQueryVo
     * @return
     */
    public List queryPeriodTimeAvgGroupByTime(DeviceQueryVo deviceQueryVo) {
        String timeEndStr = deviceQueryVo.getQueryTimeEnd();
        String timeStartStr = deviceQueryVo.getQueryTimeStart();
        if (deviceQueryVo.getTenantId() == null) {
            deviceQueryVo.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        }
        List<String> codes = deviceQueryVo.getDeviceCodeList();
        if (codes.isEmpty()) {
            return new ArrayList();
        }
        String queryStr = EsQueryString.queryPeriodTimeAvgGroupByTime;
        LinkappTenant tenant = linkappTenantService.getOneById(deviceQueryVo.getTenantId());
        if (tenant == null) {
            return Collections.emptyList();
        }
        queryStr = queryStr.replaceAll("@projectId", tenant.getProjectId());
        queryStr = queryStr.replaceAll("@getQueryTimeStart", timeStartStr);
        queryStr = queryStr.replaceAll("@getQueryTimeEnd", timeEndStr);
        if (!ObjectUtils.isEmpty(deviceQueryVo.getHasAttrs())) {
            queryStr = queryStr.replaceAll("@getHasAttrs0", deviceQueryVo.getHasAttrs().get(0));
        }
        JSONArray shouldArray = new JSONArray();
        for (String code : codes) {
            JSONObject j = new JSONObject();
            JSONObject j2 = new JSONObject();
            j2.put("deviceCode", code);
            j.put("term", j2);
            shouldArray.add(j);
        }
        queryStr = queryStr.replaceAll("\"@shouldTemplate\"", shouldArray.toJSONString());
        queryStr = queryStr.replaceAll("#orderVal", "desc");
        LOGGER.info("the queryStr : {}", queryStr);
        JSONObject paramMap = JSONObject.parseObject(queryStr);
        String indexStr;
        if (!ObjectUtils.isEmpty(deviceQueryVo.getQueryTimeStart()) && !ObjectUtils.isEmpty(deviceQueryVo.getQueryTimeEnd())) {
            indexStr = EsCalendar.getDeviceFlowIndex(deviceQueryVo.getQueryTimeStart(), deviceQueryVo.getQueryTimeEnd());
        } else {
            indexStr = EsCalendar.getDeviceFlowIndex(2);
        }
        Map r1 = getDeviceCollectDataAggFromEs2(paramMap, indexStr);
        Map aggregations = (Map) r1.get("aggregations");
        List resultList = new ArrayList();
        if (aggregations == null) {
            return resultList;
        }
        Map deviceCodeAgg = (Map) aggregations.get("aggCollectTimeGroup");
        List<Map> buckets = (List) deviceCodeAgg.get("buckets");
        for (Map bucket : buckets) {
//            日期
            String key = (String) bucket.get("key");
            Map aggsV2 = (Map) bucket.get("aggsV2");
            if (aggsV2 != null) {
                BigDecimal value = getBigDecimal(aggsV2.get("value"));
                IaqiStatistic iaqiStatistic = new IaqiStatistic();
                iaqiStatistic.setStatisticTime(DateUtil.getDateFromYYYYMMDD(key));
                iaqiStatistic.setStatisticValue(value.doubleValue());
                resultList.add(iaqiStatistic);
            }
        }
        return resultList;
    }

    @Override
    public SearchHits getDeviceDataListFromESByMsgTypeAndDeviceCodes(String projectId,
                                                                     String tenantId,
                                                                     List<String> deviceCodes, Integer msgType) {
        if (ObjectUtils.isEmpty(deviceCodes)) {
            LOGGER.info("根据数据源查得，匹配的设备集合为空，故返回es查询结果 null");
            return null;
        }
        SearchResponse searchResponse = null;
        Date startTime = DateUtil.getCurrentDayMixDate(new Date());
        Date endTime = DateUtil.getCurrentDayMaxDate(new Date());
        RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();
        SearchRequest searchRequest = new SearchRequest(EsCalendar.getDeviceFlowIndex(1));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        QueryBuilder termsQuery = QueryBuilders.termsQuery("deviceCode", deviceCodes);
        QueryBuilder boolquery = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchPhraseQuery("projectId", projectId))
                .filter(QueryBuilders.rangeQuery("createTime").gte(DateUtil.getYYYYMMDDHHMMSSDate(startTime)).lte(DateUtil.getYYYYMMDDHHMMSSDate(endTime)).format("yyyy-MM-dd HH:mm:ss"))
                .filter(QueryBuilders.termQuery("data.msg_type", msgType))
                .must(termsQuery);
        searchSourceBuilder.query(boolquery);
//        searchSourceBuilder.size(10000);

        searchRequest.source(searchSourceBuilder);
        LOGGER.info("es 查询,buildDayEnergyEachDeviceSearchResponse方法,条件是：" + searchRequest.toString());
        try {
            searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
            LOGGER.info("ES查询设备列表数据，参数：" + JSON.toJSONString(searchRequest));
            return searchResponse.getHits();
        } catch (Exception e) {
            LOGGER.error("ES查询Table列表数据异常", e);
            log.error("es request  has no  response!");
        }
        return null;
    }
}
